Merge remote-tracking branch 'origin/master' into credentials
[oweals/gnunet.git] / src / gns / gnunet-gns.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012-2013, 2017 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  * Task scheduled to handle timeout.
105  */
106 static struct GNUNET_SCHEDULER_Task *tt;
107
108
109 /**
110  * Task run on shutdown.  Cleans up everything.
111  *
112  * @param cls unused
113  */
114 static void
115 do_shutdown (void *cls)
116 {
117   if (NULL != el)
118   {
119     GNUNET_IDENTITY_ego_lookup_cancel (el);
120     el = NULL;
121   }
122   if (NULL != id_op)
123   {
124     GNUNET_IDENTITY_cancel (id_op);
125     id_op = NULL;
126   }
127   if (NULL != lookup_request)
128   {
129     GNUNET_GNS_lookup_cancel (lookup_request);
130     lookup_request = NULL;
131   }
132   if (NULL != identity)
133   {
134     GNUNET_IDENTITY_disconnect (identity);
135     identity = NULL;
136   }
137   if (NULL != gns)
138   {
139     GNUNET_GNS_disconnect (gns);
140     gns = NULL;
141   }
142   if (NULL != tt)
143   {
144     GNUNET_SCHEDULER_cancel (tt);
145     tt = NULL;
146   }
147 }
148
149
150 /**
151  * Task run on timeout. Triggers shutdown.
152  *
153  * @param cls unused
154  */
155 static void
156 do_timeout (void *cls)
157 {
158   tt = NULL;
159   GNUNET_SCHEDULER_shutdown ();
160 }
161
162
163 /**
164  * Function called with the result of a GNS lookup.
165  *
166  * @param cls the 'const char *' name that was resolved
167  * @param rd_count number of records returned
168  * @param rd array of @a rd_count records with the results
169  */
170 static void
171 process_lookup_result (void *cls,
172                        uint32_t rd_count,
173                        const struct GNUNET_GNSRECORD_Data *rd)
174 {
175   const char *name = cls;
176   uint32_t i;
177   const char *typename;
178   char* string_val;
179
180   lookup_request = NULL;
181   if (!raw)
182   {
183     if (0 == rd_count)
184       printf ("No results.\n");
185     else
186       printf ("%s:\n",
187               name);
188   }
189   for (i=0; i<rd_count; i++)
190   {
191     if ( (rd[i].record_type != rtype) &&
192          (GNUNET_GNSRECORD_TYPE_ANY != rtype) )
193       continue;
194     typename = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
195     string_val = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
196                                                    rd[i].data,
197                                                    rd[i].data_size);
198     if (NULL == string_val)
199     {
200       fprintf (stderr,
201                "Record %u of type %d malformed, skipping\n",
202                (unsigned int) i,
203                (int) rd[i].record_type);
204       continue;
205     }
206     if (raw)
207       printf ("%s\n",
208               string_val);
209     else
210       printf ("Got `%s' record: %s\n",
211               typename,
212               string_val);
213     GNUNET_free (string_val);
214   }
215   GNUNET_SCHEDULER_shutdown ();
216 }
217
218
219 /**
220  * Perform the actual resolution, starting with the zone
221  * identified by the given public key and the shorten zone.
222  *
223  * @param pkey public key to use for the zone, can be NULL
224  */
225 static void
226 lookup_with_public_key (const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
227 {
228   if (NULL != lookup_type)
229     rtype = GNUNET_GNSRECORD_typename_to_number (lookup_type);
230   else
231     rtype = GNUNET_DNSPARSER_TYPE_A;
232   if (UINT32_MAX == rtype)
233   {
234     fprintf (stderr,
235              _("Invalid typename specified, assuming `ANY'\n"));
236     rtype = GNUNET_GNSRECORD_TYPE_ANY;
237   }
238
239   if (NULL != lookup_name)
240   {
241     lookup_request = GNUNET_GNS_lookup (gns,
242                                         lookup_name,
243                                         pkey,
244                                         rtype,
245                                         local_options,
246                                         &process_lookup_result,
247                                         lookup_name);
248   }
249   else
250   {
251     fprintf (stderr,
252              _("Please specify name to lookup!\n"));
253     GNUNET_SCHEDULER_shutdown ();
254     return;
255   }
256 }
257
258
259 /**
260  * Method called to with the ego we are to use for the lookup,
261  * when the ego is determined by a name.
262  *
263  * @param cls closure (NULL, unused)
264  * @param ego ego handle, NULL if not found
265  */
266 static void
267 identity_zone_cb (void *cls,
268                   const struct GNUNET_IDENTITY_Ego *ego)
269 {
270   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
271
272   el = NULL;
273   if (NULL == ego)
274   {
275     fprintf (stderr,
276              _("Ego for `%s' not found, cannot perform lookup.\n"),
277              zone_ego_name);
278     GNUNET_SCHEDULER_shutdown ();
279   }
280   else
281   {
282     GNUNET_IDENTITY_ego_get_public_key (ego, &pkey);
283     lookup_with_public_key (&pkey);
284   }
285   GNUNET_free_non_null (zone_ego_name);
286   zone_ego_name = NULL;
287 }
288
289
290 /**
291  * Method called to with the ego we are to use for the lookup,
292  * when the ego is the one for the default master zone.
293  *
294  * @param cls closure (NULL, unused)
295  * @param ego ego handle, NULL if not found
296  * @param ctx context for application to store data for this ego
297  *                 (during the lifetime of this process, initially NULL)
298  * @param name name assigned by the user for this ego,
299  *                   NULL if the user just deleted the ego and it
300  *                   must thus no longer be used
301  */
302 static void
303 identity_master_cb (void *cls,
304                     struct GNUNET_IDENTITY_Ego *ego,
305                     void **ctx,
306                     const char *name)
307 {
308   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
309   const char *dot;
310
311   id_op = NULL;
312   if (NULL == ego)
313   {
314     fprintf (stderr,
315              _("Ego for `gns-master' not found, cannot perform lookup.  Did you run gnunet-gns-import.sh?\n"));
316     GNUNET_SCHEDULER_shutdown ();
317     return;
318   }
319   GNUNET_IDENTITY_ego_get_public_key (ego, &pkey);
320   /* main name is our own master zone, do no look for that in the DHT */
321   local_options = GNUNET_GNS_LO_LOCAL_MASTER;
322
323   /* if the name is of the form 'label.gnu', never go to the DHT */
324   dot = NULL;
325   if (NULL != lookup_name)
326     dot = strchr (lookup_name, '.');
327   if ( (NULL != dot) &&
328        (0 == strcasecmp (dot, ".gnu")) )
329     local_options = GNUNET_GNS_LO_NO_DHT;
330   lookup_with_public_key (&pkey);
331 }
332
333
334 /**
335  * Main function that will be run.
336  *
337  * @param cls closure
338  * @param args remaining command-line arguments
339  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
340  * @param c configuration
341  */
342 static void
343 run (void *cls,
344      char *const *args,
345      const char *cfgfile,
346      const struct GNUNET_CONFIGURATION_Handle *c)
347 {
348   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
349
350   cfg = c;
351   gns = GNUNET_GNS_connect (cfg);
352   if (NULL == gns)
353   {
354     fprintf (stderr,
355              _("Failed to connect to GNS\n"));
356     return;
357   }
358   tt = GNUNET_SCHEDULER_add_delayed (timeout,
359                                      &do_timeout,
360                                      NULL);
361   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
362                                  NULL);
363   identity = GNUNET_IDENTITY_connect (cfg,
364                                       NULL,
365                                       NULL);
366   if (NULL != public_key)
367   {
368     if (GNUNET_OK !=
369         GNUNET_CRYPTO_ecdsa_public_key_from_string (public_key,
370                                                     strlen (public_key),
371                                                     &pkey))
372     {
373       fprintf (stderr,
374                _("Public key `%s' is not well-formed\n"),
375                public_key);
376       GNUNET_SCHEDULER_shutdown ();
377       return;
378     }
379     lookup_with_public_key (&pkey);
380     return;
381   }
382   if (NULL != zone_ego_name)
383   {
384     el = GNUNET_IDENTITY_ego_lookup (cfg,
385                                      zone_ego_name,
386                                      &identity_zone_cb,
387                                      NULL);
388     return;
389   }
390   if ( (NULL != lookup_name) &&
391        (strlen (lookup_name) > 4) &&
392        (0 == strcmp (".zkey",
393                      &lookup_name[strlen (lookup_name) - 4])) )
394   {
395     /* no zone required, use 'anonymous' zone */
396     GNUNET_CRYPTO_ecdsa_key_get_public (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
397                                         &pkey);
398     lookup_with_public_key (&pkey);
399   }
400   else
401   {
402     GNUNET_break (NULL == id_op);
403     id_op = GNUNET_IDENTITY_get (identity,
404                                  "gns-master",
405                                  &identity_master_cb,
406                                  NULL);
407     GNUNET_assert (NULL != id_op);
408   }
409 }
410
411
412 /**
413  * The main function for gnunet-gns.
414  *
415  * @param argc number of arguments from the command line
416  * @param argv command line arguments
417  * @return 0 ok, 1 on error
418  */
419 int
420 main (int argc,
421       char *const *argv)
422 {
423   struct GNUNET_GETOPT_CommandLineOption options[] = {
424
425     GNUNET_GETOPT_option_string ('u',
426                                  "lookup",
427                                  "NAME",
428                                  gettext_noop ("Lookup a record for the given name"),
429                                  &lookup_name),
430
431     GNUNET_GETOPT_option_string ('t',
432                                  "type",
433                                  "TYPE",
434                                  gettext_noop ("Specify the type of the record to lookup"),
435                                  &lookup_type),
436
437     GNUNET_GETOPT_option_relative_time ('T',
438                                             "timeout",
439                                             "DELAY",
440                                             gettext_noop ("Specify timeout for the lookup"),
441                                             &timeout),
442
443     GNUNET_GETOPT_option_flag ('r',
444                                   "raw",
445                                   gettext_noop ("No unneeded output"),
446                                   &raw),
447
448     GNUNET_GETOPT_option_string ('p',
449                                  "public-key",
450                                  "PKEY",
451                                  gettext_noop ("Specify the public key of the zone to lookup the record in"),
452                                  &public_key),
453     
454     GNUNET_GETOPT_option_string ('z',
455                                  "zone",
456                                  "NAME",
457                                  gettext_noop ("Specify the name of the ego of the zone to lookup the record in"),
458                                  &zone_ego_name),
459
460     GNUNET_GETOPT_OPTION_END
461   };
462   int ret;
463
464   timeout = GNUNET_TIME_UNIT_FOREVER_REL;
465   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv,
466                                                  &argc, &argv))
467     return 2;
468
469   GNUNET_log_setup ("gnunet-gns",
470                     "WARNING",
471                     NULL);
472   ret =
473     (GNUNET_OK ==
474      GNUNET_PROGRAM_run (argc, argv,
475                          "gnunet-gns",
476                          _("GNUnet GNS resolver tool"),
477                          options,
478                          &run, NULL)) ? 0 : 1;
479   GNUNET_free ((void*) argv);
480   return ret;
481 }
482
483 /* end of gnunet-gns.c */