- fix
[oweals/gnunet.git] / src / peerinfo-tool / gnunet-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001-2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file peerinfo-tool/gnunet-peerinfo.c
23  * @brief Print information about other known peers.
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_crypto_lib.h"
28 #include "gnunet_configuration_lib.h"
29 #include "gnunet_getopt_lib.h"
30 #include "gnunet_program_lib.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_transport_service.h"
33 #include "gnunet_peerinfo_service.h"
34 #include "gnunet-peerinfo_plugins.h"
35
36 /**
37  * How long until we time out during peerinfo iterations?
38  */
39 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
40
41 /**
42  * Structure we use to collect printable address information.
43  */
44 struct PrintContext;
45
46 /**
47  * Record we keep for each printable address.
48  */
49 struct AddressRecord
50 {
51   /**
52    * Current address-to-string context (if active, otherwise NULL).
53    */
54   struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
55
56   /**
57    * Printable address.
58    */
59   char *result;
60   
61   /**
62    * Print context this address record belongs to.
63    */
64   struct PrintContext *pc;
65 };
66
67
68 /**
69  * Structure we use to collect printable address information.
70  */
71 struct PrintContext
72 {
73
74   /**
75    * Kept in DLL.
76    */
77   struct PrintContext *next;
78
79   /**
80    * Kept in DLL.
81    */
82   struct PrintContext *prev;
83
84   /**
85    * Identity of the peer.
86    */
87   struct GNUNET_PeerIdentity peer;
88   
89   /**
90    * List of printable addresses.
91    */
92   struct AddressRecord *address_list;
93
94   /**
95    * Number of completed addresses in 'address_list'.
96    */
97   unsigned int num_addresses;
98
99   /**
100    * Number of addresses allocated in 'address_list'.
101    */
102   unsigned int address_list_size;
103
104   /**
105    * Current offset in 'address_list' (counted down).
106    */
107   unsigned int off;
108
109 };
110
111
112 /**
113  * Option '-n'
114  */
115 static int no_resolve;
116
117 /**
118  * Option '-q'
119  */
120 static int be_quiet;
121
122 /**
123  * Option '-s'
124  */
125 static int get_self;
126
127 /**
128  * Option 
129  */
130 static int get_uri;
131
132 /**
133  * Option '-i'
134  */
135 static int get_info;
136
137 /**
138  * Option 
139  */
140 static char *put_uri;
141
142 /**
143  * Handle to peerinfo service.
144  */
145 static struct GNUNET_PEERINFO_Handle *peerinfo;
146
147 /**
148  * Configuration handle.
149  */
150 static const struct GNUNET_CONFIGURATION_Handle *cfg;
151
152 /**
153  * Main state machine task (if active).
154  */
155 static GNUNET_SCHEDULER_TaskIdentifier tt;
156
157 /**
158  * Current iterator context (if active, otherwise NULL).
159  */
160 static struct GNUNET_PEERINFO_IteratorContext *pic;
161
162 /**
163  * My peer identity.
164  */
165 static struct GNUNET_PeerIdentity my_peer_identity;
166
167 /**
168  * My public key.
169  */
170 static struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
171
172 /**
173  * Head of list of print contexts.
174  */
175 static struct PrintContext *pc_head;
176
177 /**
178  * Tail of list of print contexts.
179  */
180 static struct PrintContext *pc_tail;
181
182 /**
183  * Handle to current 'GNUNET_PEERINFO_add_peer' operation.
184  */
185 static struct GNUNET_PEERINFO_AddContext *ac;
186
187
188 /**
189  * Main state machine that goes over all options and
190  * runs the next requested function.
191  *
192  * @param cls unused
193  * @param tc unused
194  */
195 static void
196 state_machine (void *cls,
197                const struct GNUNET_SCHEDULER_TaskContext *tc);
198
199
200 /* ********************* 'get_info' ******************* */
201
202 /**
203  * Print the collected address information to the console and free 'pc'.
204  *
205  * @param pc printing context
206  */
207 static void
208 dump_pc (struct PrintContext *pc)
209 {
210   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
211   unsigned int i;
212
213   GNUNET_CRYPTO_hash_to_enc (&pc->peer.hashPubKey, &enc);
214   printf (_("Peer `%s'\n"), 
215           (const char *) &enc);
216   for (i = 0; i < pc->num_addresses; i++)
217   {
218     if (NULL != pc->address_list[i].result)
219     {
220       printf ("\t%s\n", pc->address_list[i].result);
221       GNUNET_free (pc->address_list[i].result);
222     }
223   }
224   printf ("\n");
225   GNUNET_free_non_null (pc->address_list);
226   GNUNET_CONTAINER_DLL_remove (pc_head,
227                                pc_tail,
228                                pc);
229   GNUNET_free (pc);
230   if ( (NULL == pc_head) &&
231        (NULL == pic) )
232     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);  
233 }
234
235
236 /* ************************* list all known addresses **************** */
237
238
239 /**
240  * Function to call with a human-readable format of an address
241  *
242  * @param cls closure
243  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
244  */
245 static void
246 process_resolved_address (void *cls, const char *address)
247 {
248   struct AddressRecord * ar = cls;
249   struct PrintContext *pc = ar->pc;
250
251   if (NULL != address)
252   {
253     if (NULL == ar->result)
254       ar->result = GNUNET_strdup (address);
255     return;
256   }
257   ar->atsc = NULL;
258   pc->num_addresses++;
259   if (pc->num_addresses == pc->address_list_size)
260     dump_pc (pc);
261 }
262
263
264 /**
265  * Iterator callback to go over all addresses and count them.
266  *
267  * @param cls 'struct PrintContext' with 'off' to increment
268  * @param address the address
269  * @param expiration expiration time
270  * @return GNUNET_OK to keep the address and continue
271  */
272 static int
273 count_address (void *cls, const struct GNUNET_HELLO_Address *address,
274                struct GNUNET_TIME_Absolute expiration)
275 {
276   struct PrintContext *pc = cls;
277
278   pc->off++;
279   return GNUNET_OK;
280 }
281
282
283 /**
284  * Iterator callback to go over all addresses.
285  *
286  * @param cls closure
287  * @param address the address
288  * @param expiration expiration time
289  * @return GNUNET_OK to keep the address and continue
290  */
291 static int
292 print_address (void *cls, const struct GNUNET_HELLO_Address *address,
293                struct GNUNET_TIME_Absolute expiration)
294 {
295   struct PrintContext *pc = cls;
296   struct AddressRecord *ar;
297
298   GNUNET_assert (0 < pc->off);
299   ar = &pc->address_list[--pc->off];
300   ar->pc = pc;
301   ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg, address, no_resolve,
302                                                  GNUNET_TIME_relative_multiply
303                                                  (GNUNET_TIME_UNIT_SECONDS, 10),
304                                                  &process_resolved_address, ar);
305   return GNUNET_OK;
306 }
307
308
309 /**
310  * Print information about the peer.
311  * Currently prints the GNUNET_PeerIdentity and the transport address.
312  *
313  * @param cls the 'struct PrintContext'
314  * @param peer identity of the peer 
315  * @param hello addresses of the peer
316  * @param err_msg error message
317  */
318 static void
319 print_peer_info (void *cls, const struct GNUNET_PeerIdentity *peer,
320                  const struct GNUNET_HELLO_Message *hello, const char *err_msg)
321 {
322   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
323   struct PrintContext *pc;
324
325   if (NULL == peer)
326   {
327     pic = NULL; /* end of iteration */
328     if (NULL != err_msg)
329     {
330       FPRINTF (stderr, 
331                _("Error in communication with PEERINFO service: %s\n"),
332                err_msg);
333     }
334     if (NULL == pc_head)
335       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
336     return;
337   }
338   if ((GNUNET_YES == be_quiet) || (NULL == hello))
339   {
340     GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
341     printf ("%s\n", (const char *) &enc);
342     return;
343   }
344   pc = GNUNET_malloc (sizeof (struct PrintContext));
345   GNUNET_CONTAINER_DLL_insert (pc_head,
346                                pc_tail, 
347                                pc);
348   pc->peer = *peer;
349   GNUNET_HELLO_iterate_addresses (hello, 
350                                   GNUNET_NO, 
351                                   &count_address, 
352                                   pc);
353   if (0 == pc->off)
354   {
355     dump_pc (pc);
356     return;
357   }
358   pc->address_list_size = pc->off;
359   pc->address_list = GNUNET_malloc (sizeof (struct AddressRecord) * pc->off);
360   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, 
361                                   &print_address, pc);
362 }
363
364
365 /* ************************* GET URI ************************** */
366
367
368 /**
369  * Print URI of the peer.
370  *
371  * @param cls the 'struct GetUriContext'
372  * @param peer identity of the peer (unused)
373  * @param hello addresses of the peer
374  * @param err_msg error message
375  */
376 static void
377 print_my_uri (void *cls, const struct GNUNET_PeerIdentity *peer,
378               const struct GNUNET_HELLO_Message *hello, 
379               const char *err_msg)
380 {
381   if (peer == NULL)
382   {
383     pic = NULL;
384     if (err_msg != NULL)
385       FPRINTF (stderr,
386                _("Error in communication with PEERINFO service: %s\n"), 
387                err_msg);
388     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
389     return;
390   }
391
392   if (NULL == hello)
393     return;
394
395   char *uri = GNUNET_HELLO_compose_uri(hello, &GPI_plugins_find);
396   if (NULL != uri) {
397     printf ("%s\n", (const char *) uri);
398     GNUNET_free (uri);
399   }
400 }
401
402
403 /* ************************* import HELLO by URI ********************* */
404
405
406 /**
407  * Continuation called from 'GNUNET_PEERINFO_add_peer'
408  *
409  * @param cls closure, NULL
410  * @param emsg error message, NULL on success
411  */
412 static void
413 add_continuation (void *cls,
414                   const char *emsg)
415 {
416   ac = NULL;
417   if (NULL != emsg)
418     fprintf (stderr,
419              _("Failure adding HELLO: %s\n"),
420              emsg);
421   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
422 }
423
424
425 /**
426  * Parse the PUT URI given at the command line and add it to our peerinfo 
427  * database.
428  *
429  * @param put_uri URI string to parse
430  * @return GNUNET_OK on success, GNUNET_SYSERR if the URI was invalid, GNUNET_NO on other errors
431  */
432 static int
433 parse_hello_uri (const char *put_uri)
434 {
435   struct GNUNET_HELLO_Message *hello = NULL;
436
437   int ret = GNUNET_HELLO_parse_uri(put_uri, &my_public_key, &hello, &GPI_plugins_find);
438
439   if (NULL != hello) {
440     /* WARNING: this adds the address from URI WITHOUT verification! */
441     if (GNUNET_OK == ret)
442       ac = GNUNET_PEERINFO_add_peer (peerinfo, hello, &add_continuation, NULL);
443     else
444       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
445     GNUNET_free (hello);
446   }
447
448   /* wait 1s to give peerinfo operation a chance to succeed */
449   /* FIXME: current peerinfo API sucks to require this; not to mention
450      that we get no feedback to determine if the operation actually succeeded */
451   return ret;
452 }
453
454
455 /* ************************ Main state machine ********************* */
456
457
458 /**
459  * Main state machine that goes over all options and
460  * runs the next requested function.
461  *
462  * @param cls unused
463  * @param tc scheduler context
464  */
465 static void
466 shutdown_task (void *cls,
467                const struct GNUNET_SCHEDULER_TaskContext *tc)
468 {
469   struct PrintContext *pc;
470   struct AddressRecord *ar;
471   unsigned int i;
472
473   if (NULL != ac)
474   {
475     GNUNET_PEERINFO_add_peer_cancel (ac);
476     ac = NULL;
477   }
478   if (GNUNET_SCHEDULER_NO_TASK != tt)
479   {
480     GNUNET_SCHEDULER_cancel (tt);
481     tt = GNUNET_SCHEDULER_NO_TASK;
482   }
483   if (NULL != pic)
484   {
485     GNUNET_PEERINFO_iterate_cancel (pic);
486     pic = NULL;
487   }
488   while (NULL != (pc = pc_head))
489   {
490     GNUNET_CONTAINER_DLL_remove (pc_head,
491                                  pc_tail,
492                                  pc);
493     for (i=0;i<pc->address_list_size;i++)
494     {
495       ar = &pc->address_list[i];
496       GNUNET_free_non_null (ar->result);
497       if (NULL != ar->atsc)
498       {
499         GNUNET_TRANSPORT_address_to_string_cancel (ar->atsc);
500         ar->atsc = NULL;
501       }
502     }
503     GNUNET_free_non_null (pc->address_list);
504     GNUNET_free (pc);
505   }
506   GPI_plugins_unload ();
507   if (NULL != peerinfo)
508   {
509     GNUNET_PEERINFO_disconnect (peerinfo);
510     peerinfo = NULL;
511   }
512 }
513
514
515 /**
516  * Main function that will be run by the scheduler.
517  *
518  * @param cls closure
519  * @param args remaining command-line arguments
520  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
521  * @param c configuration
522  */
523 static void
524 run (void *cls, char *const *args, const char *cfgfile,
525      const struct GNUNET_CONFIGURATION_Handle *c)
526 {
527   struct GNUNET_CRYPTO_EccPrivateKey *priv;
528   char *fn;
529
530   cfg = c;
531   if ( (NULL != args[0]) &&
532        (NULL == put_uri) &&
533        (args[0] == strcasestr (args[0], "gnunet://hello/")) )
534   {
535     put_uri = GNUNET_strdup (args[0]);
536     args++;
537   }
538   if (NULL != args[0]) 
539   {
540     FPRINTF (stderr, 
541              _("Invalid command line argument `%s'\n"), 
542              args[0]);
543     return;
544   }
545   if (NULL == (peerinfo = GNUNET_PEERINFO_connect (cfg)))
546   {
547     FPRINTF (stderr, "%s",  _("Could not access PEERINFO service.  Exiting.\n"));
548     return;
549   }
550   if ( (GNUNET_YES == get_self) || (GNUNET_YES == get_uri) )
551   {
552     /* load private key */
553     if (GNUNET_OK !=
554         GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY",
555                                                  &fn))
556     {
557       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
558                "GNUNETD", "HOSTKEYFILE");
559       return;
560     }
561     if (NULL == (priv = GNUNET_CRYPTO_ecc_key_create_from_file (fn)))
562     {
563       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
564       GNUNET_free (fn);
565       return;
566     }
567     GNUNET_free (fn);
568     GNUNET_CRYPTO_ecc_key_get_public (priv, &my_public_key);
569     GNUNET_CRYPTO_ecc_key_free (priv);
570     GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_peer_identity.hashPubKey);
571   }
572
573   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
574   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
575                                 &shutdown_task,
576                                 NULL);
577 }
578
579
580 /**
581  * Main state machine that goes over all options and
582  * runs the next requested function.
583  *
584  * @param cls unused
585  * @param tc scheduler context
586  */
587 static void
588 state_machine (void *cls,
589                const struct GNUNET_SCHEDULER_TaskContext *tc)
590 {
591   tt = GNUNET_SCHEDULER_NO_TASK;
592
593   if (NULL != put_uri)
594   {
595     GPI_plugins_load (cfg);
596     if (GNUNET_SYSERR == parse_hello_uri (put_uri))
597     {
598       fprintf (stderr,
599                _("Invalid URI `%s'\n"),
600                put_uri);
601       GNUNET_SCHEDULER_shutdown ();
602     }
603     GNUNET_free (put_uri);
604     put_uri = NULL;
605     return;
606   }
607   if (GNUNET_YES == get_info)
608   {
609     get_info = GNUNET_NO;
610     GPI_plugins_load (cfg);
611     pic = GNUNET_PEERINFO_iterate (peerinfo, NULL,
612                                    TIMEOUT,
613                                    &print_peer_info, NULL);
614     return;
615   }
616   if (GNUNET_YES == get_self)
617   {
618     struct GNUNET_CRYPTO_HashAsciiEncoded enc;
619
620     get_self = GNUNET_NO;
621     GNUNET_CRYPTO_hash_to_enc (&my_peer_identity.hashPubKey, &enc);
622     if (be_quiet)
623       printf ("%s\n", (char *) &enc);
624     else
625       printf (_("I am peer `%s'.\n"), (const char *) &enc);
626   }
627   if (GNUNET_YES == get_uri)
628   {
629     GPI_plugins_load (cfg);
630     pic = GNUNET_PEERINFO_iterate (peerinfo, &my_peer_identity,
631                                    TIMEOUT, &print_my_uri, NULL);
632     get_uri = GNUNET_NO;
633     return;
634   }
635   GNUNET_SCHEDULER_shutdown ();
636 }
637
638
639 /**
640  * The main function to obtain peer information.
641  *
642  * @param argc number of arguments from the command line
643  * @param argv command line arguments
644  * @return 0 ok, 1 on error
645  */
646 int
647 main (int argc, char *const *argv)
648 {
649   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
650     {'n', "numeric", NULL,
651      gettext_noop ("don't resolve host names"),
652      0, &GNUNET_GETOPT_set_one, &no_resolve},
653     {'q', "quiet", NULL,
654      gettext_noop ("output only the identity strings"),
655      0, &GNUNET_GETOPT_set_one, &be_quiet},
656     {'s', "self", NULL,
657      gettext_noop ("output our own identity only"),
658      0, &GNUNET_GETOPT_set_one, &get_self},
659     {'i', "info", NULL,
660      gettext_noop ("list all known peers"),
661      0, &GNUNET_GETOPT_set_one, &get_info},
662     {'g', "get-hello", NULL,
663      gettext_noop ("also output HELLO uri(s)"),
664      0, &GNUNET_GETOPT_set_one, &get_uri},
665     {'p', "put-hello", "HELLO",
666      gettext_noop ("add given HELLO uri to the database"),
667      1, &GNUNET_GETOPT_set_string, &put_uri},
668     GNUNET_GETOPT_OPTION_END
669   };
670   int ret;
671
672   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
673     return 2;
674
675   ret = (GNUNET_OK ==
676          GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
677                              gettext_noop ("Print information about peers."),
678                              options, &run, NULL)) ? 0 : 1;
679   GNUNET_free ((void*) argv);
680   return ret;
681 }
682
683 /* end of gnunet-peerinfo.c */