-LRN: initialize rd to avoid having garbage in flags
[oweals/gnunet.git] / src / namestore / gnunet-namestore.c
1 /*
2      This file is part of GNUnet.
3      (C) 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  * @file gnunet-namestore.c
22  * @brief command line tool to manipulate the local zone
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - allow users to set record options (not just 'RF_AUTHORITY')
27  * - test
28  * - add options to list/lookup individual records
29  */
30 #include "platform.h"
31 #include <gnunet_util_lib.h>
32 #include <gnunet_dnsparser_lib.h>
33 #include <gnunet_namestore_service.h>
34
35
36 /**
37  * Handle to the namestore.
38  */
39 static struct GNUNET_NAMESTORE_Handle *ns;
40
41 /**
42  * Hash of the public key of our zone.
43  */
44 static struct GNUNET_CRYPTO_ShortHashCode zone;
45
46 /**
47  * Private key for the our zone.
48  */
49 static struct GNUNET_CRYPTO_RsaPrivateKey *zone_pkey;
50
51 /**
52  * Keyfile to manipulate.
53  */
54 static char *keyfile;   
55
56 /**
57  * Desired action is to add a record.
58  */
59 static int add;
60
61 /**
62  * Queue entry for the 'add' operation.
63  */
64 static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
65
66 /**
67  * Queue entry for the 'add-uri' operation.
68  */
69 static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri;
70
71 /**
72  * Desired action is to list records.
73  */
74 static int list;
75
76 /**
77  * List iterator for the 'list' operation.
78  */
79 static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
80
81 /**
82  * Desired action is to remove a record.
83  */
84 static int del;
85
86 /**
87  * Is record public
88  */
89 static int public;
90
91 /**
92  * Is record authority
93  */
94 static int nonauthority;
95
96 /**
97  * Queue entry for the 'del' operation.
98  */
99 static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
100
101 /**
102  * Name of the records to add/list/remove.
103  */
104 static char *name;
105
106 /**
107  * Value of the record to add/remove.
108  */
109 static char *value;
110
111 /**
112  * URI to import.
113  */
114 static char *uri;
115
116 /**
117  * Type of the record to add/remove, NULL to remove all.
118  */
119 static char *typestring;
120
121 /**
122  * Desired expiration time.
123  */
124 static char *expirationstring;
125
126 /**
127  * Global return value
128  */
129 static int ret;
130
131
132 /**
133  * Task run on shutdown.  Cleans up everything.
134  *
135  * @param cls unused
136  * @param tc scheduler context
137  */
138 static void
139 do_shutdown (void *cls,
140              const struct GNUNET_SCHEDULER_TaskContext *tc)
141 {
142   if (NULL != list_it)
143   {
144     GNUNET_NAMESTORE_zone_iteration_stop (list_it);
145     list_it = NULL;
146   }
147   if (NULL != add_qe)
148   {
149     GNUNET_NAMESTORE_cancel (add_qe);
150     add_qe = NULL;
151   }
152   if (NULL != add_qe_uri)
153   {
154     GNUNET_NAMESTORE_cancel (add_qe_uri);
155     add_qe_uri = NULL;
156   }
157   if (NULL != del_qe)
158   {
159     GNUNET_NAMESTORE_cancel (del_qe);
160     del_qe = NULL;
161   }
162   if (NULL != ns)
163   {
164     GNUNET_NAMESTORE_disconnect (ns);
165     ns = NULL;
166   }
167   if (NULL != zone_pkey)
168   {
169     GNUNET_CRYPTO_rsa_key_free (zone_pkey);
170     zone_pkey = NULL;
171   }
172   if (NULL != uri)
173   {
174     GNUNET_free (uri);
175     uri = NULL;
176   }
177 }
178
179
180 /**
181  * Continuation called to notify client about result of the
182  * operation.
183  *
184  * @param cls closure, location of the QueueEntry pointer to NULL out
185  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
186  *                GNUNET_NO if content was already there
187  *                GNUNET_YES (or other positive value) on success
188  * @param emsg NULL on success, otherwise an error message
189  */
190 static void
191 add_continuation (void *cls,
192                   int32_t success,
193                   const char *emsg)
194 {
195   struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
196
197   *qe = NULL;
198   if (GNUNET_YES != success)
199   {
200     fprintf (stderr,
201              _("Adding record failed: %s\n"),
202              (GNUNET_NO == success) ? "record exists" : emsg);
203     if (GNUNET_NO != success)
204       ret = 1;
205   }
206   if ( (NULL == add_qe) &&
207        (NULL == add_qe_uri) &&
208        (NULL == del_qe) &&
209        (NULL == list_it) )
210     GNUNET_SCHEDULER_shutdown ();
211 }
212
213
214 /**
215  * Continuation called to notify client about result of the
216  * operation.
217  *
218  * @param cls closure, unused
219  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
220  *                GNUNET_NO if content was already there
221  *                GNUNET_YES (or other positive value) on success
222  * @param emsg NULL on success, otherwise an error message
223  */
224 static void
225 del_continuation (void *cls,
226                   int32_t success,
227                   const char *emsg)
228 {
229   del_qe = NULL;
230   if (success != GNUNET_YES)
231     fprintf (stderr,
232              _("Deleting record failed: %s\n"),
233              emsg);
234   if ( (NULL == add_qe) &&
235        (NULL == add_qe_uri) &&
236        (NULL == list_it) )
237     GNUNET_SCHEDULER_shutdown ();
238 }
239
240
241 /**
242  * Process a record that was stored in the namestore.
243  *
244  * @param cls closure
245  * @param zone_key public key of the zone
246  * @param expire when does the corresponding block in the DHT expire (until
247  *               when should we never do a DHT lookup for the same name again)?; 
248  *               GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
249  *               or the expiration time of the block in the namestore (even if there are zero
250  *               records matching the desired record type)
251  * @param name name that is being mapped (at most 255 characters long)
252  * @param rd_len number of entries in 'rd' array
253  * @param rd array of records with data to store
254  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
255  *        because the user queried for a particular record type only)
256  */
257 static void
258 display_record (void *cls,
259                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
260                 struct GNUNET_TIME_Absolute expire,                         
261                 const char *name,
262                 unsigned int rd_len,
263                 const struct GNUNET_NAMESTORE_RecordData *rd,
264                 const struct GNUNET_CRYPTO_RsaSignature *signature)
265 {
266   const char *typestring;
267   char *s;
268   unsigned int i;
269   char *etime;
270   struct GNUNET_TIME_Absolute aex;
271   struct GNUNET_TIME_Relative rex;
272
273   if (NULL == name)
274   {
275     list_it = NULL;
276     if ( (NULL == del_qe) &&
277          (NULL == add_qe_uri) &&
278          (NULL == add_qe) )
279       GNUNET_SCHEDULER_shutdown ();
280     return;
281   }
282   FPRINTF (stdout,
283            "%s:\n",
284            name);
285   for (i=0;i<rd_len;i++)
286   {
287     typestring = GNUNET_NAMESTORE_number_to_typename (rd[i].record_type);
288     s = GNUNET_NAMESTORE_value_to_string (rd[i].record_type,
289                                           rd[i].data,
290                                           rd[i].data_size);
291     if (NULL == s)
292     {
293       FPRINTF (stdout, _("\tCorrupt or unsupported record of type %u\n"),
294                (unsigned int) rd[i].record_type);
295       continue;
296     }
297     if (0 != (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION))
298     {
299       rex.rel_value = rd[i].expiration_time;
300       etime = GNUNET_STRINGS_relative_time_to_string (rex);
301     }
302     else
303     {
304       aex.abs_value = rd[i].expiration_time;
305       etime = GNUNET_STRINGS_absolute_time_to_string (aex);
306     }
307     FPRINTF (stdout, "\t%s: %s (%s %s)\n", typestring, s, 
308              (0 != (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION)) 
309              ? _(/* what follows is relative expiration */ "for at least")
310              : _(/* what follows is absolute expiration */ "until"),
311              etime);
312     GNUNET_free (etime);
313     GNUNET_free (s);    
314   }
315   FPRINTF (stdout, "%s", "\n");
316   GNUNET_NAMESTORE_zone_iterator_next (list_it);
317 }
318
319
320 /**
321  * Main function that will be run.
322  *
323  * @param cls closure
324  * @param args remaining command-line arguments
325  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
326  * @param cfg configuration
327  */
328 static void
329 run (void *cls, char *const *args, const char *cfgfile,
330      const struct GNUNET_CONFIGURATION_Handle *cfg)
331 {
332   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
333   uint32_t type;
334   void *data = NULL;
335   size_t data_size = 0;
336   struct GNUNET_TIME_Relative etime_rel;
337   struct GNUNET_TIME_Absolute etime_abs;
338   int etime_is_rel = GNUNET_SYSERR;
339   struct GNUNET_NAMESTORE_RecordData rd;
340
341   if ( (NULL != args[0]) && (NULL == uri) )
342     uri = GNUNET_strdup (args[0]);
343   if (NULL == keyfile)
344   {
345     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
346                                                               "ZONEKEY", &keyfile))
347     {
348       fprintf (stderr,
349                _("Option `%s' not given, but I need a zone key file!\n"),
350                "z");
351       return;
352     }
353     fprintf (stderr,
354              _("Using default zone file `%s'\n"),
355              keyfile);
356   }
357   zone_pkey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
358   GNUNET_free (keyfile);
359   keyfile = NULL;
360   if (! (add|del|list|(NULL != uri)))
361   {
362     /* nothing more to be done */  
363     fprintf (stderr,
364              _("No options given\n"));
365     GNUNET_CRYPTO_rsa_key_free (zone_pkey);
366     zone_pkey = NULL;
367     return; 
368   }
369   if (NULL == zone_pkey)
370   {
371     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
372                 _("Failed to read or create private zone key\n"));
373     return;
374   }
375   GNUNET_CRYPTO_rsa_key_get_public (zone_pkey,
376                                     &pub);
377   GNUNET_CRYPTO_short_hash (&pub, sizeof (pub), &zone);
378
379   ns = GNUNET_NAMESTORE_connect (cfg);
380   if (NULL == ns)
381   {
382     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
383                 _("Failed to connect to namestore\n"));
384     return;
385   }
386   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
387                                 &do_shutdown, NULL);
388   if (NULL == typestring)
389     type = 0;
390   else
391     type = GNUNET_NAMESTORE_typename_to_number (typestring);
392   if (UINT32_MAX == type)
393   {
394     fprintf (stderr, _("Unsupported type `%s'\n"), typestring);
395     GNUNET_SCHEDULER_shutdown ();
396     ret = 1;
397     return;
398   }
399   if ((NULL == typestring) && (add | del))
400   {
401     fprintf (stderr,
402              _("Missing option `%s' for operation `%s'\n"),
403              "-t", _("add/del"));
404     GNUNET_SCHEDULER_shutdown ();
405     ret = 1;
406     return;     
407   }
408   if (NULL != value)
409   {
410     if (GNUNET_OK !=
411         GNUNET_NAMESTORE_string_to_value (type,
412                                           value,
413                                           &data,
414                                           &data_size))
415       {
416         fprintf (stderr, _("Value `%s' invalid for record type `%s'\n"), 
417                  value,
418                  typestring);
419         GNUNET_SCHEDULER_shutdown ();
420         ret = 1;
421         return;
422       }
423   } else if (add | del)
424   {
425     fprintf (stderr,
426              _("Missing option `%s' for operation `%s'\n"),
427              "-V", _("add/del"));
428     ret = 1;   
429     GNUNET_SCHEDULER_shutdown ();
430     return;     
431   }
432   if (NULL != expirationstring)
433   {
434     if (0 == strcmp (expirationstring, "never"))
435     {
436       etime_abs = GNUNET_TIME_UNIT_FOREVER_ABS;
437       etime_is_rel = GNUNET_NO;
438     }
439     else if (GNUNET_OK ==
440              GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
441                                                     &etime_rel))
442     {
443       etime_is_rel = GNUNET_YES;
444     }
445     else if (GNUNET_OK == 
446              GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
447                                                     &etime_abs))
448     {
449       etime_is_rel = GNUNET_NO;
450     }
451     else
452     {
453       fprintf (stderr,
454                _("Invalid time format `%s'\n"),
455                expirationstring);
456       GNUNET_SCHEDULER_shutdown ();
457       ret = 1;
458       return;     
459     }
460   } 
461   else if (add)
462   {
463     fprintf (stderr,
464              _("Missing option `%s' for operation `%s'\n"),
465              "-e", _("add"));
466     GNUNET_SCHEDULER_shutdown ();
467     ret = 1;    
468     return;     
469   }
470   memset (&rd, 0, sizeof (rd));
471   if (add)
472   {
473     if (NULL == name)
474     {
475       fprintf (stderr,
476                _("Missing option `%s' for operation `%s'\n"),
477                "-n", _("add"));
478       GNUNET_SCHEDULER_shutdown ();
479       ret = 1;    
480       return;     
481     }
482     rd.data = data;
483     rd.data_size = data_size;
484     rd.record_type = type;
485     if (GNUNET_YES == etime_is_rel)
486     {
487       rd.expiration_time = etime_rel.rel_value;
488       rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
489     }
490     else if (GNUNET_NO == etime_is_rel)
491       rd.expiration_time = etime_abs.abs_value;
492     else
493     {
494       fprintf (stderr,
495                _("No valid expiration time for operation `%s'\n"),
496                _("add"));
497       GNUNET_SCHEDULER_shutdown ();
498       ret = 1;
499       return;
500     }
501     if (1 != nonauthority)
502       rd.flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
503     if (1 != public)
504       rd.flags |= GNUNET_NAMESTORE_RF_PRIVATE;
505     add_qe = GNUNET_NAMESTORE_record_create (ns,
506                                              zone_pkey,
507                                              name,
508                                              &rd,
509                                              &add_continuation,
510                                              &add_qe);
511   }
512   if (del)
513   {
514     if (NULL == name)
515     {
516       fprintf (stderr,
517                _("Missing option `%s' for operation `%s'\n"),
518                "-n", _("del"));
519       GNUNET_SCHEDULER_shutdown ();
520       ret = 1;
521       return;     
522     }
523     rd.data = data;
524     rd.data_size = data_size;
525     rd.record_type = type;
526     rd.expiration_time = 0;
527     rd.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
528     del_qe = GNUNET_NAMESTORE_record_remove (ns,
529                                              zone_pkey,
530                                              name,
531                                              &rd,
532                                              &del_continuation,
533                                              NULL);
534   }
535   if (list)
536   {
537     uint32_t must_not_flags = 0;
538
539     if (1 == nonauthority) /* List non-authority records */
540       must_not_flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
541
542     if (1 == public)
543       must_not_flags |= GNUNET_NAMESTORE_RF_PRIVATE;
544
545     list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
546                                                      &zone,
547                                                      GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION,
548                                                      must_not_flags,
549                                                      &display_record,
550                                                      NULL);
551   }
552   if (NULL != uri)
553   {
554     char sh[53];
555     char name[64];
556     struct GNUNET_CRYPTO_ShortHashCode sc;
557
558     if ( (2 != (sscanf (uri,
559                         "gnunet://gns/%52s/%63s",
560                         sh,
561                         name)) ) ||
562          (GNUNET_OK !=
563           GNUNET_CRYPTO_short_hash_from_string (sh, &sc)) )
564     {
565       fprintf (stderr, 
566                _("Invalid URI `%s'\n"),
567                uri);
568       GNUNET_SCHEDULER_shutdown ();
569       ret = 1;
570       return;
571     }
572     rd.data = &sc;
573     rd.data_size = sizeof (struct GNUNET_CRYPTO_ShortHashCode);
574     rd.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
575     if (GNUNET_YES == etime_is_rel)
576     {
577       rd.expiration_time = etime_rel.rel_value;
578       rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
579     }
580     else if (GNUNET_NO == etime_is_rel)
581       rd.expiration_time = etime_abs.abs_value;
582     else    
583       rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value;
584     if (1 != nonauthority)
585       rd.flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
586
587     add_qe_uri = GNUNET_NAMESTORE_record_create (ns,
588                                                  zone_pkey,
589                                                  name,
590                                                  &rd,
591                                                  &add_continuation,
592                                                  &add_qe_uri);
593   }
594   GNUNET_free_non_null (data);
595 }
596
597
598 /**
599  * The main function for gnunet-namestore.
600  *
601  * @param argc number of arguments from the command line
602  * @param argv command line arguments
603  * @return 0 ok, 1 on error
604  */
605 int
606 main (int argc, char *const *argv)
607 {
608   nonauthority = -1;
609   public = -1;
610
611   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
612     {'a', "add", NULL,
613      gettext_noop ("add record"), 0,
614      &GNUNET_GETOPT_set_one, &add},
615     {'d', "delete", NULL,
616      gettext_noop ("delete record"), 0,
617      &GNUNET_GETOPT_set_one, &del},   
618     {'D', "display", NULL,
619      gettext_noop ("display records"), 0,
620      &GNUNET_GETOPT_set_one, &list},   
621     {'e', "expiration", "TIME",
622      gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"), 1,
623      &GNUNET_GETOPT_set_string, &expirationstring},   
624     {'n', "name", "NAME",
625      gettext_noop ("name of the record to add/delete/display"), 1,
626      &GNUNET_GETOPT_set_string, &name},   
627     {'t', "type", "TYPE",
628      gettext_noop ("type of the record to add/delete/display"), 1,
629      &GNUNET_GETOPT_set_string, &typestring},   
630     {'u', "uri", "URI",
631      gettext_noop ("URI to import into our zone"), 1,
632      &GNUNET_GETOPT_set_string, &uri},   
633     {'V', "value", "VALUE",
634      gettext_noop ("value of the record to add/delete"), 1,
635      &GNUNET_GETOPT_set_string, &value},   
636     {'p', "public", NULL,
637      gettext_noop ("create or list public record"), 0,
638      &GNUNET_GETOPT_set_one, &public},
639     {'N', "non-authority", NULL,
640      gettext_noop ("create or list non-authority record"), 0,
641      &GNUNET_GETOPT_set_one, &nonauthority},
642     {'z', "zonekey", "FILENAME",
643      gettext_noop ("filename with the zone key"), 1,
644      &GNUNET_GETOPT_set_string, &keyfile},   
645     GNUNET_GETOPT_OPTION_END
646   };
647
648   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
649     return 2;
650
651   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
652   if (GNUNET_OK !=
653       GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
654                           _("GNUnet zone manipulation tool"), 
655                           options,
656                           &run, NULL))
657     return 1;
658
659   return ret;
660 }
661
662 /* end of gnunet-namestore.c */