-fixeS
[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   if (NULL != hello)
274   {
275     GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &count_address, pc);
276     GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &compose_uri, pc);
277   }
278   printf ("%s\n", pc->uri);
279 }
280
281 struct GNUNET_PEERINFO_HelloAddressParsingContext
282 {
283   char *tmp;
284   char *pos;
285   size_t tmp_len;
286 };
287
288 static size_t
289 add_addr_to_hello (void *cls, size_t max, void *buffer)
290 {
291   struct tm expiration_time;
292   char buf[5];
293   long l;
294   time_t expiration_seconds;
295   struct GNUNET_TIME_Absolute expire;
296
297   struct GNUNET_PEERINFO_HelloAddressParsingContext *ctx = cls;
298   char *exp1, *exp2;
299   struct GNUNET_TRANSPORT_PluginFunctions *papi;
300   void *addr;
301   size_t addr_len;
302
303   /* End of string */
304   if (ctx->pos - ctx->tmp == ctx->tmp_len)
305     return 0;
306
307   /* Parsed past the end of string, OR wrong format */
308   if ((ctx->pos - ctx->tmp > ctx->tmp_len) || ctx->pos[0] != '!')
309   {
310     GNUNET_break (0);
311     return 0;
312   }
313
314   /* Not enough bytes (3 for three '!', 14 for expiration date, and
315    * at least 1 for type and 1 for address (1-byte long address is a joke,
316    * but it is not completely unrealistic. Zero-length address is.
317    */
318   if (ctx->tmp_len - (ctx->pos - ctx->tmp) < 1 /*!*/ * 3 + 14 + /* at least */ 2)
319   {
320     GNUNET_break (0);
321     return 0;
322   }
323   /* Go past the first '!', now we're on expiration date */
324   ctx->pos += 1;
325   /* Its length is known, so check for the next '!' right away */
326   if (ctx->pos[14] != '!')
327   {
328     GNUNET_break (0);
329     return 0;
330   }
331
332   memset (&expiration_time, 0, sizeof (struct tm));
333
334   /* This is FAR more strict than strptime(ctx->pos, "%Y%m%d%H%M%S", ...); */
335   /* FIXME: make it a separate function, since expiration is specified to every address */
336 #define GETNDIGITS(n,cond) \
337   strncpy (buf, &ctx->pos[0], n); \
338   buf[n] = '\0'; \
339   errno = 0; \
340   l = strtol (buf, NULL, 10); \
341   if (errno != 0 || cond) \
342   { \
343     GNUNET_break (0); \
344     return 0; \
345   } \
346   ctx->pos += n;
347
348   GETNDIGITS (4, l < 1900)
349   expiration_time.tm_year = l - 1900;
350
351   GETNDIGITS (2, l < 1 || l > 12)
352   expiration_time.tm_mon = l;
353
354   GETNDIGITS (2, l < 1 || l > 31)
355   expiration_time.tm_mday = l;
356
357   GETNDIGITS (2, l < 0 || l > 23)
358   expiration_time.tm_hour = l;
359
360   GETNDIGITS (2, l < 0 || l > 59)
361   expiration_time.tm_min = l;
362
363   /* 60 - with a leap second */
364   GETNDIGITS (2, l < 0 || l > 60)
365   expiration_time.tm_sec = l;
366
367   expiration_time.tm_isdst = -1;
368
369 #undef GETNDIGITS
370
371   expiration_seconds = mktime (&expiration_time);
372   if (expiration_seconds == (time_t) -1)
373   {
374     GNUNET_break (0);
375     return 0;
376   }
377   expire.abs_value = expiration_seconds * 1000;
378
379   /* Now we're at '!', advance to the transport type */
380   ctx->pos += 1;
381
382   /* Find the next '!' that separates transport type from
383    * the address
384    */
385   exp1 = strstr (ctx->pos, "!");
386   if (exp1 == NULL)
387   {
388     GNUNET_break (0);
389     return 0;
390   }
391   /* We need it 0-terminated */
392   exp1[0] = '\0';
393   /* Find the '!' that separates address from the next record.
394    * It might not be there, if this is the last record.
395    */
396   exp2 = strstr (&exp1[1], "!");
397   if (exp2 == NULL)
398     exp2 = &ctx->tmp[ctx->tmp_len];
399
400   papi = GST_plugins_find (ctx->pos);
401   if (papi == NULL)
402   {
403     /* Not an error - we might just not have the right plugin.
404      * Skip this part, advance to the next one and recurse.
405      * But only if this is not the end of string.
406      */
407     ctx->pos = exp2 + 1;
408     if (ctx->pos - ctx->tmp >= ctx->tmp_len)
409       return 0;
410     return add_addr_to_hello (cls, max, buffer);
411   }
412
413   if (GNUNET_OK == papi->string_to_address (papi->cls, &exp1[1], exp2 - &exp1[1], &addr, &addr_len))
414   {
415     struct GNUNET_HELLO_Address address;
416     int ret;
417
418     /* address.peer is unset - not used by add_address() */
419     address.address_length = addr_len;
420     address.address = addr;
421     address.transport_name = ctx->pos;
422     ret = GNUNET_HELLO_add_address (&address, expire, buffer, max);
423     GNUNET_free (addr);
424     ctx->pos = exp2;
425     return ret;
426   }
427   return 0;
428 }
429
430 void
431 parse_hello (const struct GNUNET_CONFIGURATION_Handle *c,
432              const char *put_uri)
433 {
434   int r;
435   char *scheme_part = NULL;
436   char *path_part = NULL;
437   char *exc;
438   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
439   int std_result;
440   struct GNUNET_HELLO_Message *hello;
441   struct GNUNET_PEERINFO_HelloAddressParsingContext ctx;
442
443   r = GNUNET_STRINGS_parse_uri (put_uri, &scheme_part, (const char **) &path_part);
444   if (r == GNUNET_NO)
445     return;
446   if (scheme_part == NULL || strcmp (scheme_part, "gnunet://") != 0)
447   {
448     GNUNET_free_non_null (scheme_part);
449     return;
450   }
451   GNUNET_free (scheme_part);
452
453   if (strncmp (path_part, "hello/", 6) != 0)
454     return;
455
456   path_part = &path_part[6];
457   ctx.tmp = GNUNET_strdup (path_part);
458   ctx.tmp_len = strlen (path_part);
459   exc = strstr (ctx.tmp, "!");
460   if (exc == NULL)
461     exc = ctx.pos + ctx.tmp_len; // FIXME-LRN: ctx.pos is uninitialized here!
462   ctx.pos = exc;
463
464   std_result = GNUNET_STRINGS_string_to_data (ctx.tmp, exc - ctx.tmp,
465       (unsigned char *) &pub, sizeof (pub));
466   if (std_result != GNUNET_OK)
467   {
468     GNUNET_free (ctx.tmp);
469     return;
470   }
471
472   hello = GNUNET_HELLO_create (&pub, add_addr_to_hello, &ctx);
473   GNUNET_free (ctx.tmp);
474
475   /* WARNING: this adds the address from URI WITHOUT verification! */
476   GNUNET_PEERINFO_add_peer (peerinfo, hello);
477   GNUNET_free (hello);
478 }
479
480 static struct GNUNET_TIME_Relative
481 receive_stub (void *cls, const struct GNUNET_PeerIdentity *peer,
482     const struct GNUNET_MessageHeader *message,
483     const struct GNUNET_ATS_Information *ats, uint32_t ats_count,
484     struct Session *session, const char *sender_address,
485     uint16_t sender_address_len)
486 {
487   struct GNUNET_TIME_Relative t;
488   t.rel_value = 0;
489   return t;
490 }
491
492 static void
493 address_notification_stub (void *cls, int add_remove,
494     const void *addr, size_t addrlen)
495 {
496 }
497
498 static void
499 session_end_stub (void *cls, const struct GNUNET_PeerIdentity *peer,
500     struct Session * session)
501 {
502 }
503
504 static const struct GNUNET_ATS_Information
505 address_to_type_stub (void *cls, const struct sockaddr *addr,
506     size_t addrlen)
507 {
508   struct GNUNET_ATS_Information t;
509   t.type = 0;
510   t.value = 0;
511   return t;
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_RsaPrivateKey *priv;
528   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
529   struct GNUNET_PeerIdentity pid;
530   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
531   char *fn;
532
533   cfg = c;
534   if (args[0] != NULL)
535   {
536     FPRINTF (stderr, _("Invalid command line argument `%s'\n"), args[0]);
537     return;
538   }
539   if (put_uri != NULL && get_uri == GNUNET_YES)
540   {
541     FPRINTF (stderr, "%s", _("--put-uri and --get-uri are mutually exclusive\n"));
542     return;
543   }
544   if (put_uri != NULL || get_uri == GNUNET_YES || get_self != GNUNET_YES)
545   {
546     peerinfo = GNUNET_PEERINFO_connect (cfg);
547     if (peerinfo == NULL)
548     {
549       FPRINTF (stderr, "%s",  _("Could not access PEERINFO service.  Exiting.\n"));
550       return;
551     }
552     GST_cfg = c;
553     GST_stats = GNUNET_STATISTICS_create ("transport", c);
554     /* FIXME: shouldn't we free GST_stats somewhere? */
555     GST_plugins_load (receive_stub, address_notification_stub,
556         session_end_stub, address_to_type_stub);
557   }
558   if (put_uri != NULL)
559   {
560     parse_hello (c, put_uri);
561     GST_plugins_unload ();
562     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
563     return;
564   }
565   if (get_self != GNUNET_YES)
566   {
567     GNUNET_PEERINFO_iterate (peerinfo, NULL,
568                              GNUNET_TIME_relative_multiply
569                              (GNUNET_TIME_UNIT_SECONDS, 5), &print_peer_info,
570                              NULL);
571   }
572   else
573   {
574     if (GNUNET_OK !=
575         GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
576                                                  &fn))
577     {
578       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
579                "GNUNETD", "HOSTKEYFILE");
580       return;
581     }
582     priv = GNUNET_CRYPTO_rsa_key_create_from_file (fn);
583     if (priv == NULL)
584     {
585       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
586       GNUNET_free (fn);
587       return;
588     }
589     GNUNET_free (fn);
590     GNUNET_CRYPTO_rsa_key_get_public (priv, &pub);
591     GNUNET_CRYPTO_rsa_key_free (priv);
592     GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey);
593     GNUNET_CRYPTO_hash_to_enc (&pid.hashPubKey, &enc);
594     if (be_quiet)
595       printf ("%s\n", (char *) &enc);
596     else
597       printf (_("I am peer `%s'.\n"), (const char *) &enc);
598     if (get_uri == GNUNET_YES)
599     {
600       struct PrintContext *pc;
601       char *pkey;
602       ssize_t l, pl;
603
604       pc = GNUNET_malloc (sizeof (struct PrintContext));
605       pkey = GNUNET_CRYPTO_rsa_public_key_to_string (&pub);
606       pl = strlen ("gnunet://hello/");
607       l = strlen (pkey) + pl;
608       pc->uri = GNUNET_malloc (l + 1);
609       strcpy (pc->uri, "gnunet://hello/");
610       strcpy (&pc->uri[pl], pkey);
611       pc->uri_len = l;
612       GNUNET_PEERINFO_iterate (peerinfo, &pid,
613           GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5),
614           print_my_uri, pc);
615       GNUNET_free (pc->uri);
616       GNUNET_free (pc);
617     }
618   }
619 }
620
621
622 /**
623  * The main function to obtain peer information.
624  *
625  * @param argc number of arguments from the command line
626  * @param argv command line arguments
627  * @return 0 ok, 1 on error
628  */
629 int
630 main (int argc, char *const *argv)
631 {
632   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
633     {'n', "numeric", NULL,
634      gettext_noop ("don't resolve host names"),
635      0, &GNUNET_GETOPT_set_one, &no_resolve},
636     {'q', "quiet", NULL,
637      gettext_noop ("output only the identity strings"),
638      0, &GNUNET_GETOPT_set_one, &be_quiet},
639     {'s', "self", NULL,
640      gettext_noop ("output our own identity only"),
641      0, &GNUNET_GETOPT_set_one, &get_self},
642     {'g', "get-hello", NULL,
643      gettext_noop ("also output HELLO uri(s)"),
644      0, &GNUNET_GETOPT_set_one, &get_uri},
645     {'p', "put-hello", "HELLO",
646      gettext_noop ("add given HELLO uri to the database"),
647      1, &GNUNET_GETOPT_set_string, &put_uri},
648     GNUNET_GETOPT_OPTION_END
649   };
650   return (GNUNET_OK ==
651           GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
652                               gettext_noop ("Print information about peers."),
653                               options, &run, NULL)) ? 0 : 1;
654 }
655
656 /* end of gnunet-peerinfo.c */