22df4f7a1dbd6466700b5ce115bf1ba45061b03d
[oweals/gnunet.git] / src / peerinfo-tool / gnunet-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2003, 2004, 2006, 2009, 2010 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_peerinfo_service.h"
31 #include "gnunet_transport_service.h"
32 #include "gnunet_program_lib.h"
33 #include "gnunet_transport_plugin.h"
34 #include "../transport/gnunet-service-transport_plugins.h"
35
36 static int no_resolve;
37
38 static int be_quiet;
39
40 static int get_self;
41
42 static int get_uri;
43
44 static char *put_uri;
45
46 static struct GNUNET_PEERINFO_Handle *peerinfo;
47
48 /**
49  * Configuration handle.
50  */
51 const struct GNUNET_CONFIGURATION_Handle *GST_cfg;
52
53 /**
54  * Statistics handle.
55  */
56 struct GNUNET_STATISTICS_Handle *GST_stats;
57
58 /**
59  * Configuration handle.
60  */
61 struct GNUNET_PeerIdentity GST_my_identity;
62
63 struct GNUNET_MessageHeader *our_hello = NULL;
64
65 static const struct GNUNET_CONFIGURATION_Handle *cfg;
66
67 struct PrintContext
68 {
69   struct GNUNET_PeerIdentity peer;
70   char **address_list;
71   unsigned int num_addresses;
72   uint32_t off;
73   char *uri;
74   size_t uri_len;
75 };
76
77 /**
78  * Obtain this peers HELLO message.
79  *
80  * @return our HELLO message
81  */
82 const struct GNUNET_MessageHeader *
83 GST_hello_get ()
84 {
85   return (struct GNUNET_MessageHeader *) our_hello;
86 }
87
88 static void
89 dump_pc (struct PrintContext *pc)
90 {
91   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
92   unsigned int i;
93
94   GNUNET_CRYPTO_hash_to_enc (&pc->peer.hashPubKey, &enc);
95   printf (_("Peer `%s'\n"), (const char *) &enc);
96   for (i = 0; i < pc->num_addresses; i++)
97   {
98     printf ("\t%s\n", pc->address_list[i]);
99     GNUNET_free (pc->address_list[i]);
100   }
101   printf ("\n");
102   GNUNET_array_grow (pc->address_list, pc->num_addresses, 0);
103   GNUNET_free (pc);
104 }
105
106
107 /**
108  * Function to call with a human-readable format of an address
109  *
110  * @param cls closure
111  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
112  */
113 static void
114 process_resolved_address (void *cls, const char *address)
115 {
116   struct PrintContext *pc = cls;
117
118   if (address == NULL)
119   {
120     pc->off--;
121     if (pc->off == 0)
122       dump_pc (pc);
123     return;
124   }
125   GNUNET_array_append (pc->address_list, pc->num_addresses,
126                        GNUNET_strdup (address));
127 }
128
129
130 /**
131  * Iterator callback to go over all addresses.
132  *
133  * @param cls closure
134  * @param address the address
135  * @param expiration expiration time
136  * @return GNUNET_OK to keep the address and continue
137  */
138 static int
139 count_address (void *cls, const struct GNUNET_HELLO_Address *address,
140                struct GNUNET_TIME_Absolute expiration)
141 {
142   struct PrintContext *pc = cls;
143
144   pc->off++;
145   return GNUNET_OK;
146 }
147
148
149 /**
150  * Iterator callback to go over all addresses.
151  *
152  * @param cls closure
153  * @param address the address
154  * @param expiration expiration time
155  * @return GNUNET_OK to keep the address and continue
156  */
157 static int
158 print_address (void *cls, const struct GNUNET_HELLO_Address *address,
159                struct GNUNET_TIME_Absolute expiration)
160 {
161   struct PrintContext *pc = cls;
162
163   GNUNET_TRANSPORT_address_to_string (cfg, address, no_resolve,
164                                       GNUNET_TIME_relative_multiply
165                                       (GNUNET_TIME_UNIT_SECONDS, 10),
166                                       &process_resolved_address, pc);
167   return GNUNET_OK;
168 }
169
170
171 /**
172  * Print information about the peer.
173  * Currently prints the GNUNET_PeerIdentity and the IP.
174  * Could of course do more (e.g. resolve via DNS).
175  */
176 static void
177 print_peer_info (void *cls, const struct GNUNET_PeerIdentity *peer,
178                  const struct GNUNET_HELLO_Message *hello, const char *err_msg)
179 {
180   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
181   struct PrintContext *pc;
182
183   if (peer == NULL)
184   {
185     if (err_msg != NULL)
186       FPRINTF (stderr, "%s",  _("Error in communication with PEERINFO service\n"));
187     GNUNET_PEERINFO_disconnect (peerinfo);
188     GST_plugins_unload ();
189     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
190     return;
191   }
192   if ((be_quiet) || (NULL == hello))
193   {
194     GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
195     printf ("%s\n", (const char *) &enc);
196     return;
197   }
198   pc = GNUNET_malloc (sizeof (struct PrintContext));
199   pc->peer = *peer;
200   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &count_address, pc);
201   if (0 == pc->off)
202   {
203     dump_pc (pc);
204     return;
205   }
206   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &print_address, pc);
207 }
208
209 static int
210 compose_uri (void *cls, const struct GNUNET_HELLO_Address *address,
211              struct GNUNET_TIME_Absolute expiration)
212 {
213   struct PrintContext *pc = cls;
214   struct GNUNET_TRANSPORT_PluginFunctions *papi;
215   static const char *addr;
216
217   papi = GST_plugins_find (address->transport_name);
218   if (papi == NULL)
219   {
220     /* Not an error - we might just not have the right plugin. */
221     return GNUNET_OK;
222   }
223
224   addr = papi->address_to_string (papi->cls, address->address, address->address_length);
225   if (addr != NULL)
226   {
227     ssize_t l = strlen (addr);
228     if (l > 0)
229     {
230       struct tm *t;
231       time_t seconds;
232       int s;
233       seconds = expiration.abs_value / 1000;
234       t = gmtime(&seconds);
235       pc->uri = GNUNET_realloc (pc->uri, pc->uri_len + 1 + 14 + 1 + strlen (address->transport_name) + 1 + l + 1 /* 0 */);
236       s = sprintf (&pc->uri[pc->uri_len], "!%04u%02u%02u%02u%02u%02u!%s!%s",
237           t->tm_year, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
238           address->transport_name, addr);
239       if (s > 0)
240         pc->uri_len += s;
241     }
242   }
243   return GNUNET_OK;
244 }
245
246 /**
247  * Print information about the peer.
248  */
249 static void
250 print_my_uri (void *cls, const struct GNUNET_PeerIdentity *peer,
251               const struct GNUNET_HELLO_Message *hello, const char *err_msg)
252 {
253   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
254   struct PrintContext *pc = cls;
255
256   if (peer == NULL)
257   {
258     if (err_msg != NULL)
259       FPRINTF (stderr, "%s",  _("Error in communication with PEERINFO service\n"));
260     GNUNET_PEERINFO_disconnect (peerinfo);
261     GST_plugins_unload ();
262     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
263     return;
264   }
265   if ((be_quiet) || (NULL == hello))
266   {
267     GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
268     printf ("%s\n", (const char *) &enc);
269     if (be_quiet && get_uri != GNUNET_YES)
270       return;
271   }
272   pc->peer = *peer;
273   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &count_address, pc);
274   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &compose_uri, pc);
275   printf ("%s\n", pc->uri);
276   GNUNET_free (pc->uri);
277   GNUNET_free (pc);
278 }
279
280 struct GNUNET_PEERINFO_HelloAddressParsingContext
281 {
282   char *tmp;
283   char *pos;
284   size_t tmp_len;
285 };
286
287 static size_t
288 add_addr_to_hello (void *cls, size_t max, void *buffer)
289 {
290   struct tm expiration_time;
291   char buf[5];
292   long l;
293   time_t expiration_seconds;
294   struct GNUNET_TIME_Absolute expire;
295
296   struct GNUNET_PEERINFO_HelloAddressParsingContext *ctx = cls;
297   char *exp1, *exp2;
298   struct GNUNET_TRANSPORT_PluginFunctions *papi;
299   void *addr;
300   size_t addr_len;
301
302   /* End of string */
303   if (ctx->pos - ctx->tmp == ctx->tmp_len)
304     return 0;
305
306   /* Parsed past the end of string, OR wrong format */
307   if ((ctx->pos - ctx->tmp > ctx->tmp_len) || ctx->pos[0] != '!')
308   {
309     GNUNET_break (0);
310     return 0;
311   }
312
313   /* Not enough bytes (3 for three '!', 14 for expiration date, and
314    * at least 1 for type and 1 for address (1-byte long address is a joke,
315    * but it is not completely unrealistic. Zero-length address is.
316    */
317   if (ctx->tmp_len - (ctx->pos - ctx->tmp) < 1 /*!*/ * 3 + 14 + /* at least */ 2)
318   {
319     GNUNET_break (0);
320     return 0;
321   }
322   /* Go past the first '!', now we're on expiration date */
323   ctx->pos += 1;
324   /* Its length is known, so check for the next '!' right away */
325   if (ctx->pos[14] != '!')
326   {
327     GNUNET_break (0);
328     return 0;
329   }
330
331   memset (&expiration_time, 0, sizeof (struct tm));
332
333   /* This is FAR more strict than strptime(ctx->pos, "%Y%m%d%H%M%S", ...); */
334   /* FIXME: make it a separate function, since expiration is specified to every address */
335 #define GETNDIGITS(n,cond) \
336   strncpy (buf, &ctx->pos[0], n); \
337   buf[n] = '\0'; \
338   errno = 0; \
339   l = strtol (buf, NULL, 10); \
340   if (errno != 0 || cond) \
341   { \
342     GNUNET_break (0); \
343     return 0; \
344   } \
345   ctx->pos += n;
346
347   GETNDIGITS (4, l < 1900)
348   expiration_time.tm_year = l - 1900;
349
350   GETNDIGITS (2, l < 1 || l > 12)
351   expiration_time.tm_mon = l;
352
353   GETNDIGITS (2, l < 1 || l > 31)
354   expiration_time.tm_mday = l;
355
356   GETNDIGITS (2, l < 0 || l > 23)
357   expiration_time.tm_hour = l;
358
359   GETNDIGITS (2, l < 0 || l > 59)
360   expiration_time.tm_min = l;
361
362   /* 60 - with a leap second */
363   GETNDIGITS (2, l < 0 || l > 60)
364   expiration_time.tm_sec = l;
365
366   expiration_time.tm_isdst = -1;
367
368 #undef GETNDIGITS
369
370   expiration_seconds = mktime (&expiration_time);
371   if (expiration_seconds == (time_t) -1)
372   {
373     GNUNET_break (0);
374     return 0;
375   }
376   expire.abs_value = expiration_seconds * 1000;
377
378   /* Now we're at '!', advance to the transport type */
379   ctx->pos += 1;
380
381   /* Find the next '!' that separates transport type from
382    * the address
383    */
384   exp1 = strstr (ctx->pos, "!");
385   if (exp1 == NULL)
386   {
387     GNUNET_break (0);
388     return 0;
389   }
390   /* We need it 0-terminated */
391   exp1[0] = '\0';
392   /* Find the '!' that separates address from the next record.
393    * It might not be there, if this is the last record.
394    */
395   exp2 = strstr (&exp1[1], "!");
396   if (exp2 == NULL)
397     exp2 = &ctx->tmp[ctx->tmp_len];
398
399   papi = GST_plugins_find (ctx->pos);
400   if (papi == NULL)
401   {
402     /* Not an error - we might just not have the right plugin.
403      * Skip this part, advance to the next one and recurse.
404      * But only if this is not the end of string.
405      */
406     ctx->pos = exp2 + 1;
407     if (ctx->pos - ctx->tmp >= ctx->tmp_len)
408       return 0;
409     return add_addr_to_hello (cls, max, buffer);
410   }
411
412   if (GNUNET_OK == papi->string_to_address (papi->cls, &exp1[1], exp2 - &exp1[1], &addr, &addr_len))
413   {
414     struct GNUNET_HELLO_Address address;
415     int ret;
416
417     /* address.peer is unset - not used by add_address() */
418     address.address_length = addr_len;
419     address.address = addr;
420     address.transport_name = ctx->pos;
421     ret = GNUNET_HELLO_add_address (&address, expire, buffer, max);
422     GNUNET_free (addr);
423     ctx->pos = exp2;
424     return ret;
425   }
426   return 0;
427 }
428
429 void
430 parse_hello (const struct GNUNET_CONFIGURATION_Handle *c,
431              const char *put_uri)
432 {
433   int r;
434   char *scheme_part = NULL;
435   char *path_part = NULL;
436   char *exc;
437   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
438   int std_result;
439   struct GNUNET_HELLO_Message *hello;
440   struct GNUNET_PEERINFO_HelloAddressParsingContext ctx;
441
442   r = GNUNET_STRINGS_parse_uri (put_uri, &scheme_part, (const char **) &path_part);
443   if (r == GNUNET_NO)
444     return;
445   if (scheme_part == NULL || strcmp (scheme_part, "gnunet://") != 0)
446   {
447     GNUNET_free_non_null (scheme_part);
448     return;
449   }
450   GNUNET_free (scheme_part);
451
452   if (strncmp (path_part, "hello/", 6) != 0)
453     return;
454
455   path_part = &path_part[6];
456   ctx.tmp = GNUNET_strdup (path_part);
457   ctx.tmp_len = strlen (path_part);
458   exc = strstr (ctx.tmp, "!");
459   if (exc == NULL)
460     exc = ctx.pos + ctx.tmp_len;
461   ctx.pos = exc;
462
463   std_result = GNUNET_STRINGS_string_to_data (ctx.tmp, exc - ctx.tmp,
464       (unsigned char *) &pub, sizeof (pub));
465   if (std_result != GNUNET_OK)
466   {
467     GNUNET_free (ctx.tmp);
468     return;
469   }
470
471   hello = GNUNET_HELLO_create (&pub, add_addr_to_hello, &ctx);
472   GNUNET_free (ctx.tmp);
473
474   /* WARNING: this adds the address from URI WITHOUT verification! */
475   GNUNET_PEERINFO_add_peer (peerinfo, hello);
476 }
477
478 static struct GNUNET_TIME_Relative
479 receive_stub (void *cls, const struct GNUNET_PeerIdentity *peer,
480     const struct GNUNET_MessageHeader *message,
481     const struct GNUNET_ATS_Information *ats, uint32_t ats_count,
482     struct Session *session, const char *sender_address,
483     uint16_t sender_address_len)
484 {
485   struct GNUNET_TIME_Relative t;
486   t.rel_value = 0;
487   return t;
488 }
489
490 static void
491 address_notification_stub (void *cls, int add_remove,
492     const void *addr, size_t addrlen)
493 {
494 }
495
496 static void
497 session_end_stub (void *cls, const struct GNUNET_PeerIdentity *peer,
498     struct Session * session)
499 {
500 }
501
502 static const struct GNUNET_ATS_Information
503 address_to_type_stub (void *cls, const struct sockaddr *addr,
504     size_t addrlen)
505 {
506   struct GNUNET_ATS_Information t;
507   t.type = 0;
508   t.value = 0;
509   return t;
510 }
511
512
513 /**
514  * Main function that will be run by the scheduler.
515  *
516  * @param cls closure
517  * @param args remaining command-line arguments
518  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
519  * @param c configuration
520  */
521 static void
522 run (void *cls, char *const *args, const char *cfgfile,
523      const struct GNUNET_CONFIGURATION_Handle *c)
524 {
525   struct GNUNET_CRYPTO_RsaPrivateKey *priv;
526   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
527   struct GNUNET_PeerIdentity pid;
528   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
529   char *fn;
530
531   cfg = c;
532   if (args[0] != NULL)
533   {
534     FPRINTF (stderr, _("Invalid command line argument `%s'\n"), args[0]);
535     return;
536   }
537   if (put_uri != NULL && get_uri == GNUNET_YES)
538   {
539     FPRINTF (stderr, "%s", _("--put-uri and --get-uri are mutually exclusive\n"));
540     return;
541   }
542   if (put_uri != NULL || get_uri == GNUNET_YES || get_self != GNUNET_YES)
543   {
544     peerinfo = GNUNET_PEERINFO_connect (cfg);
545     if (peerinfo == NULL)
546     {
547       FPRINTF (stderr, "%s",  _("Could not access PEERINFO service.  Exiting.\n"));
548       return;
549     }
550     GST_cfg = c;
551     GST_stats = GNUNET_STATISTICS_create ("transport", c);
552     /* FIXME: shouldn't we free GST_stats somewhere? */
553     GST_plugins_load (receive_stub, address_notification_stub,
554         session_end_stub, address_to_type_stub);
555   }
556   if (put_uri != NULL)
557   {
558     parse_hello (c, put_uri);
559     GST_plugins_unload ();
560     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
561     return;
562   }
563   if (get_self != GNUNET_YES)
564   {
565     GNUNET_PEERINFO_iterate (peerinfo, NULL,
566                              GNUNET_TIME_relative_multiply
567                              (GNUNET_TIME_UNIT_SECONDS, 5), &print_peer_info,
568                              NULL);
569   }
570   else
571   {
572     if (GNUNET_OK !=
573         GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
574                                                  &fn))
575     {
576       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
577                "GNUNETD", "HOSTKEYFILE");
578       return;
579     }
580     priv = GNUNET_CRYPTO_rsa_key_create_from_file (fn);
581     if (priv == NULL)
582     {
583       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
584       GNUNET_free (fn);
585       return;
586     }
587     GNUNET_free (fn);
588     GNUNET_CRYPTO_rsa_key_get_public (priv, &pub);
589     GNUNET_CRYPTO_rsa_key_free (priv);
590     GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey);
591     GNUNET_CRYPTO_hash_to_enc (&pid.hashPubKey, &enc);
592     if (be_quiet)
593       printf ("%s\n", (char *) &enc);
594     else
595       printf (_("I am peer `%s'.\n"), (const char *) &enc);
596     if (get_uri == GNUNET_YES)
597     {
598       struct PrintContext *pc;
599       char *pkey;
600       ssize_t l, pl;
601       pc = GNUNET_malloc (sizeof (struct PrintContext));
602       pkey = GNUNET_CRYPTO_rsa_public_key_to_string (&pub);
603       pl = strlen ("gnunet://hello/");
604       l = strlen (pkey) + pl;
605       pc->uri = GNUNET_malloc (l + 1);
606       strcpy (pc->uri, "gnunet://hello/");
607       strcpy (&pc->uri[pl], pkey);
608       pc->uri_len = l;
609       GNUNET_PEERINFO_iterate (peerinfo, &pid,
610           GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5),
611           print_my_uri, pc);
612     }
613   }
614 }
615
616
617 /**
618  * The main function to obtain peer information.
619  *
620  * @param argc number of arguments from the command line
621  * @param argv command line arguments
622  * @return 0 ok, 1 on error
623  */
624 int
625 main (int argc, char *const *argv)
626 {
627   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
628     {'n', "numeric", NULL,
629      gettext_noop ("don't resolve host names"),
630      0, &GNUNET_GETOPT_set_one, &no_resolve},
631     {'q', "quiet", NULL,
632      gettext_noop ("output only the identity strings"),
633      0, &GNUNET_GETOPT_set_one, &be_quiet},
634     {'s', "self", NULL,
635      gettext_noop ("output our own identity only"),
636      0, &GNUNET_GETOPT_set_one, &get_self},
637     {'g', "get-hello", NULL,
638      gettext_noop ("also output HELLO uri(s)"),
639      0, &GNUNET_GETOPT_set_one, &get_uri},
640     {'p', "put-hello", "HELLO",
641      gettext_noop ("add given HELLO uri to the database"),
642      1, &GNUNET_GETOPT_set_string, &put_uri},
643     GNUNET_GETOPT_OPTION_END
644   };
645   return (GNUNET_OK ==
646           GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
647                               gettext_noop ("Print information about peers."),
648                               options, &run, NULL)) ? 0 : 1;
649 }
650
651 /* end of gnunet-peerinfo.c */