5d083cdb6bddad7bf0981b92503f62fb4ee90e29
[oweals/gnunet.git] / src / gns / gnunet-gns.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012-2013 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file gnunet-gns.c
22  * @brief command line tool to access distributed GNS
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include <gnunet_util_lib.h>
27 #include <gnunet_dnsparser_lib.h>
28 #include <gnunet_identity_service.h>
29 #include <gnunet_gnsrecord_lib.h>
30 #include <gnunet_namestore_service.h>
31 #include <gnunet_gns_service.h>
32
33 /**
34  * Configuration we are using.
35  */
36 static const struct GNUNET_CONFIGURATION_Handle *cfg;
37
38 /**
39  * Handle to GNS service.
40  */
41 static struct GNUNET_GNS_Handle *gns;
42
43 /**
44  * Desired timeout for the lookup (default is no timeout).
45  */
46 static struct GNUNET_TIME_Relative timeout;
47
48 /**
49  * GNS name to lookup. (-u option)
50  */
51 static char *lookup_name;
52
53 /**
54  * record type to look up (-t option)
55  */
56 static char *lookup_type;
57
58 /**
59  * Identity of the zone to use for the lookup (-z option)
60  */
61 static char *zone_ego_name;
62
63 /**
64  * Public key of the zone to use for the lookup (-p option)
65  */
66 static char *public_key;
67
68 /**
69  * Set to GNUNET_GNS_LO_LOCAL_MASTER if we are looking up in the master zone.
70  */
71 static enum GNUNET_GNS_LocalOptions local_options;
72
73 /**
74  * raw output
75  */
76 static int raw;
77
78 /**
79  * Requested record type.
80  */
81 static int rtype;
82
83 /**
84  * Handle to lookup request
85  */
86 static struct GNUNET_GNS_LookupRequest *lookup_request;
87
88 /**
89  * Lookup an ego with the identity service.
90  */
91 static struct GNUNET_IDENTITY_EgoLookup *el;
92
93 /**
94  * Handle for identity service.
95  */
96 static struct GNUNET_IDENTITY_Handle *identity;
97
98 /**
99  * Active operation on identity service.
100  */
101 static struct GNUNET_IDENTITY_Operation *id_op;
102
103
104 /**
105  * Task run on shutdown.  Cleans up everything.
106  *
107  * @param cls unused
108  * @param tc scheduler context
109  */
110 static void
111 do_shutdown (void *cls,
112              const struct GNUNET_SCHEDULER_TaskContext *tc)
113 {
114   if (NULL != el)
115   {
116     GNUNET_IDENTITY_ego_lookup_cancel (el);
117     el = NULL;
118   }
119   if (NULL != id_op)
120   {
121     GNUNET_IDENTITY_cancel (id_op);
122     id_op = NULL;
123   }
124   if (NULL != lookup_request)
125   {
126     GNUNET_GNS_lookup_cancel (lookup_request);
127     lookup_request = NULL;
128   }
129   if (NULL != identity)
130   {
131     GNUNET_IDENTITY_disconnect (identity);
132     identity = NULL;
133   }
134   if (NULL != gns)
135   {
136     GNUNET_GNS_disconnect (gns);
137     gns = NULL;
138   }
139 }
140
141
142 /**
143  * Function called with the result of a GNS lookup.
144  *
145  * @param cls the 'const char *' name that was resolved
146  * @param rd_count number of records returned
147  * @param rd array of @a rd_count records with the results
148  */
149 static void
150 process_lookup_result (void *cls, uint32_t rd_count,
151                        const struct GNUNET_GNSRECORD_Data *rd)
152 {
153   const char *name = cls;
154   uint32_t i;
155   const char *typename;
156   char* string_val;
157
158   lookup_request = NULL;
159   if (!raw)
160   {
161     if (0 == rd_count)
162       printf ("No results.\n");
163     else
164       printf ("%s:\n",
165               name);
166   }
167   for (i=0; i<rd_count; i++)
168   {
169     if ( (rd[i].record_type != rtype) &&
170          (GNUNET_GNSRECORD_TYPE_ANY != rtype) )
171       continue;
172     typename = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
173     string_val = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
174                                                    rd[i].data,
175                                                    rd[i].data_size);
176     if (NULL == string_val)
177     {
178       fprintf (stderr,
179                "Record %u of type %d malformed, skipping\n",
180                (unsigned int) i,
181                (int) rd[i].record_type);
182       continue;
183     }
184     if (raw)
185       printf ("%s\n",
186               string_val);
187     else
188       printf ("Got `%s' record: %s\n",
189               typename,
190               string_val);
191     GNUNET_free (string_val);
192   }
193   GNUNET_SCHEDULER_shutdown ();
194 }
195
196
197 /**
198  * Perform the actual resolution, starting with the zone
199  * identified by the given public key and the shorten zone.
200  *
201  * @param pkey public key to use for the zone, can be NULL
202  * @param shorten_key private key used for shortening, can be NULL
203  */
204 static void
205 lookup_with_keys (const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey,
206                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key)
207 {
208   if (NULL != lookup_type)
209     rtype = GNUNET_GNSRECORD_typename_to_number (lookup_type);
210   else
211     rtype = GNUNET_DNSPARSER_TYPE_A;
212   if (UINT32_MAX == rtype)
213   {
214     fprintf (stderr,
215              _("Invalid typename specified, assuming `ANY'\n"));
216     rtype = GNUNET_GNSRECORD_TYPE_ANY;
217   }
218
219   if (NULL != lookup_name)
220   {
221     lookup_request = GNUNET_GNS_lookup (gns,
222                                         lookup_name,
223                                         pkey,
224                                         rtype,
225                                         local_options,
226                                         shorten_key,
227                                         &process_lookup_result,
228                                         lookup_name);
229   }
230   else
231   {
232     fprintf (stderr,
233              _("Please specify name to lookup!\n"));
234     GNUNET_SCHEDULER_shutdown ();
235     return;
236   }
237 }
238
239
240 /**
241  * Method called to with the ego we are to use for shortening
242  * during the lookup.
243  *
244  * @param cls closure contains the public key to use
245  * @param ego ego handle, NULL if not found
246  * @param ctx context for application to store data for this ego
247  *                 (during the lifetime of this process, initially NULL)
248  * @param name name assigned by the user for this ego,
249  *                   NULL if the user just deleted the ego and it
250  *                   must thus no longer be used
251  */
252 static void
253 identity_shorten_cb (void *cls,
254                      struct GNUNET_IDENTITY_Ego *ego,
255                      void **ctx,
256                      const char *name)
257 {
258   struct GNUNET_CRYPTO_EcdsaPublicKey *pkeym = cls;
259
260   id_op = NULL;
261   if (NULL == ego)
262     lookup_with_keys (pkeym, NULL);
263   else
264     lookup_with_keys (pkeym,
265                       GNUNET_IDENTITY_ego_get_private_key (ego));
266   GNUNET_free (pkeym);
267 }
268
269
270 /**
271  * Perform the actual resolution, starting with the zone
272  * identified by the given public key.
273  *
274  * @param pkey public key to use for the zone
275  */
276 static void
277 lookup_with_public_key (const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
278 {
279   struct GNUNET_CRYPTO_EcdsaPublicKey *pkeym;
280
281   GNUNET_assert (NULL != pkey);
282   pkeym = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPublicKey);
283   *pkeym = *pkey;
284   GNUNET_break (NULL == id_op);
285   id_op = GNUNET_IDENTITY_get (identity,
286                                "gns-short",
287                                &identity_shorten_cb,
288                                pkeym);
289   if (NULL == id_op)
290   {
291     GNUNET_break (0);
292     lookup_with_keys (pkey, NULL);
293   }
294 }
295
296
297 /**
298  * Method called to with the ego we are to use for the lookup,
299  * when the ego is determined by a name.
300  *
301  * @param cls closure (NULL, unused)
302  * @param ego ego handle, NULL if not found
303  */
304 static void
305 identity_zone_cb (void *cls,
306                   const struct GNUNET_IDENTITY_Ego *ego)
307 {
308   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
309
310   el = NULL;
311   if (NULL == ego)
312   {
313     fprintf (stderr,
314              _("Ego for `%s' not found, cannot perform lookup.\n"),
315              zone_ego_name);
316     GNUNET_SCHEDULER_shutdown ();
317   }
318   else
319   {
320     GNUNET_IDENTITY_ego_get_public_key (ego, &pkey);
321     lookup_with_public_key (&pkey);
322   }
323   GNUNET_free_non_null (zone_ego_name);
324   zone_ego_name = NULL;
325 }
326
327
328 /**
329  * Method called to with the ego we are to use for the lookup,
330  * when the ego is the one for the default master zone.
331  *
332  * @param cls closure (NULL, unused)
333  * @param ego ego handle, NULL if not found
334  * @param ctx context for application to store data for this ego
335  *                 (during the lifetime of this process, initially NULL)
336  * @param name name assigned by the user for this ego,
337  *                   NULL if the user just deleted the ego and it
338  *                   must thus no longer be used
339  */
340 static void
341 identity_master_cb (void *cls,
342                     struct GNUNET_IDENTITY_Ego *ego,
343                     void **ctx,
344                     const char *name)
345 {
346   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
347   const char *dot;
348
349   id_op = NULL;
350   if (NULL == ego)
351   {
352     fprintf (stderr,
353              _("Ego for `gns-master' not found, cannot perform lookup.  Did you run gnunet-gns-import.sh?\n"));
354     GNUNET_SCHEDULER_shutdown ();
355     return;
356   }
357   GNUNET_IDENTITY_ego_get_public_key (ego, &pkey);
358   /* main name is our own master zone, do no look for that in the DHT */
359   local_options = GNUNET_GNS_LO_LOCAL_MASTER;
360
361   /* if the name is of the form 'label.gnu', never go to the DHT */
362   dot = NULL;
363   if (NULL != lookup_name)
364     dot = strchr (lookup_name, '.');
365   if ( (NULL != dot) &&
366        (0 == strcasecmp (dot, ".gnu")) )
367     local_options = GNUNET_GNS_LO_NO_DHT;
368   lookup_with_public_key (&pkey);
369 }
370
371
372 /**
373  * Main function that will be run.
374  *
375  * @param cls closure
376  * @param args remaining command-line arguments
377  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
378  * @param c configuration
379  */
380 static void
381 run (void *cls, char *const *args, const char *cfgfile,
382      const struct GNUNET_CONFIGURATION_Handle *c)
383 {
384   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
385
386   cfg = c;
387   gns = GNUNET_GNS_connect (cfg);
388   identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL);
389   if (NULL == gns)
390   {
391     fprintf (stderr,
392              _("Failed to connect to GNS\n"));
393     return;
394   }
395   GNUNET_SCHEDULER_add_delayed (timeout,
396                                 &do_shutdown, NULL);
397   if (NULL != public_key)
398   {
399     if (GNUNET_OK !=
400         GNUNET_CRYPTO_ecdsa_public_key_from_string (public_key,
401                                                   strlen (public_key),
402                                                   &pkey))
403     {
404       fprintf (stderr,
405                _("Public key `%s' is not well-formed\n"),
406                public_key);
407       GNUNET_SCHEDULER_shutdown ();
408       return;
409     }
410     lookup_with_public_key (&pkey);
411     return;
412   }
413   if (NULL != zone_ego_name)
414   {
415     el = GNUNET_IDENTITY_ego_lookup (cfg,
416                                      zone_ego_name,
417                                      &identity_zone_cb,
418                                      NULL);
419     return;
420   }
421   if ( (NULL != lookup_name) &&
422        (strlen (lookup_name) > 4) &&
423        (0 == strcmp (".zkey",
424                      &lookup_name[strlen (lookup_name) - 4])) )
425   {
426     /* no zone required, use 'anonymous' zone */
427     GNUNET_CRYPTO_ecdsa_key_get_public (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
428                                       &pkey);
429     lookup_with_public_key (&pkey);
430   }
431   else
432   {
433     GNUNET_break (NULL == id_op);
434     id_op = GNUNET_IDENTITY_get (identity,
435                                  "gns-master",
436                                  &identity_master_cb,
437                                  NULL);
438     GNUNET_assert (NULL != id_op);
439   }
440 }
441
442
443 /**
444  * The main function for gnunet-gns.
445  *
446  * @param argc number of arguments from the command line
447  * @param argv command line arguments
448  * @return 0 ok, 1 on error
449  */
450 int
451 main (int argc, char *const *argv)
452 {
453   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
454     {'u', "lookup", "NAME",
455       gettext_noop ("Lookup a record for the given name"), 1,
456       &GNUNET_GETOPT_set_string, &lookup_name},
457     {'t', "type", "TYPE",
458       gettext_noop ("Specify the type of the record to lookup"), 1,
459       &GNUNET_GETOPT_set_string, &lookup_type},
460     { 'T', "timeout", "DELAY",
461       gettext_noop ("Specify timeout for the lookup"), 1,
462       &GNUNET_GETOPT_set_relative_time, &timeout },
463     {'r', "raw", NULL,
464       gettext_noop ("No unneeded output"), 0,
465       &GNUNET_GETOPT_set_one, &raw},
466     {'p', "public-key", "PKEY",
467       gettext_noop ("Specify the public key of the zone to lookup the record in"), 1,
468       &GNUNET_GETOPT_set_string, &public_key},
469     {'z', "zone", "NAME",
470       gettext_noop ("Specify the name of the ego of the zone to lookup the record in"), 1,
471       &GNUNET_GETOPT_set_string, &zone_ego_name},
472     GNUNET_GETOPT_OPTION_END
473   };
474   int ret;
475
476   timeout = GNUNET_TIME_UNIT_FOREVER_REL;
477   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
478     return 2;
479
480   GNUNET_log_setup ("gnunet-gns", "WARNING", NULL);
481   ret =
482       (GNUNET_OK ==
483        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns",
484                            _("GNUnet GNS resolver tool"),
485                            options,
486                            &run, NULL)) ? 0 : 1;
487   GNUNET_free ((void*) argv);
488   return ret;
489 }
490
491 /* end of gnunet-gns.c */