-LRN: Correct time == 0 handling:
[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     if (etime_is_rel && del)
461     {
462       fprintf (stderr,
463                _("Deletion requires either absolute time, or no time at all. Got relative time `%s' instead.\n"),
464                expirationstring);
465       GNUNET_SCHEDULER_shutdown ();
466       ret = 1;
467       return;
468     }
469   } 
470   else if (add)
471   {
472     fprintf (stderr,
473              _("Missing option `%s' for operation `%s'\n"),
474              "-e", _("add"));
475     GNUNET_SCHEDULER_shutdown ();
476     ret = 1;    
477     return;     
478   }
479   memset (&rd, 0, sizeof (rd));
480   if (add)
481   {
482     if (NULL == name)
483     {
484       fprintf (stderr,
485                _("Missing option `%s' for operation `%s'\n"),
486                "-n", _("add"));
487       GNUNET_SCHEDULER_shutdown ();
488       ret = 1;    
489       return;     
490     }
491     rd.data = data;
492     rd.data_size = data_size;
493     rd.record_type = type;
494     if (GNUNET_YES == etime_is_rel)
495     {
496       rd.expiration_time = etime_rel.rel_value;
497       rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
498     }
499     else if (GNUNET_NO == etime_is_rel)
500       rd.expiration_time = etime_abs.abs_value;
501     else
502     {
503       fprintf (stderr,
504                _("No valid expiration time for operation `%s'\n"),
505                _("add"));
506       GNUNET_SCHEDULER_shutdown ();
507       ret = 1;
508       return;
509     }
510     if (1 != nonauthority)
511       rd.flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
512     if (1 != public)
513       rd.flags |= GNUNET_NAMESTORE_RF_PRIVATE;
514     add_qe = GNUNET_NAMESTORE_record_create (ns,
515                                              zone_pkey,
516                                              name,
517                                              &rd,
518                                              &add_continuation,
519                                              &add_qe);
520   }
521   if (del)
522   {
523     if (NULL == name)
524     {
525       fprintf (stderr,
526                _("Missing option `%s' for operation `%s'\n"),
527                "-n", _("del"));
528       GNUNET_SCHEDULER_shutdown ();
529       ret = 1;
530       return;     
531     }
532     rd.data = data;
533     rd.data_size = data_size;
534     rd.record_type = type;
535     rd.expiration_time = 0;
536     if (!etime_is_rel)
537       rd.expiration_time = etime_abs.abs_value;
538     rd.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
539     del_qe = GNUNET_NAMESTORE_record_remove (ns,
540                                              zone_pkey,
541                                              name,
542                                              &rd,
543                                              &del_continuation,
544                                              NULL);
545   }
546   if (list)
547   {
548     uint32_t must_not_flags = 0;
549
550     if (1 == nonauthority) /* List non-authority records */
551       must_not_flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
552
553     if (1 == public)
554       must_not_flags |= GNUNET_NAMESTORE_RF_PRIVATE;
555
556     list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
557                                                      &zone,
558                                                      GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION,
559                                                      must_not_flags,
560                                                      &display_record,
561                                                      NULL);
562   }
563   if (NULL != uri)
564   {
565     char sh[53];
566     char name[64];
567     struct GNUNET_CRYPTO_ShortHashCode sc;
568
569     if ( (2 != (sscanf (uri,
570                         "gnunet://gns/%52s/%63s",
571                         sh,
572                         name)) ) ||
573          (GNUNET_OK !=
574           GNUNET_CRYPTO_short_hash_from_string (sh, &sc)) )
575     {
576       fprintf (stderr, 
577                _("Invalid URI `%s'\n"),
578                uri);
579       GNUNET_SCHEDULER_shutdown ();
580       ret = 1;
581       return;
582     }
583     rd.data = &sc;
584     rd.data_size = sizeof (struct GNUNET_CRYPTO_ShortHashCode);
585     rd.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
586     if (GNUNET_YES == etime_is_rel)
587     {
588       rd.expiration_time = etime_rel.rel_value;
589       rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
590     }
591     else if (GNUNET_NO == etime_is_rel)
592       rd.expiration_time = etime_abs.abs_value;
593     else    
594       rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value;
595     if (1 != nonauthority)
596       rd.flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
597
598     add_qe_uri = GNUNET_NAMESTORE_record_create (ns,
599                                                  zone_pkey,
600                                                  name,
601                                                  &rd,
602                                                  &add_continuation,
603                                                  &add_qe_uri);
604   }
605   GNUNET_free_non_null (data);
606 }
607
608
609 /**
610  * The main function for gnunet-namestore.
611  *
612  * @param argc number of arguments from the command line
613  * @param argv command line arguments
614  * @return 0 ok, 1 on error
615  */
616 int
617 main (int argc, char *const *argv)
618 {
619   nonauthority = -1;
620   public = -1;
621
622   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
623     {'a', "add", NULL,
624      gettext_noop ("add record"), 0,
625      &GNUNET_GETOPT_set_one, &add},
626     {'d', "delete", NULL,
627      gettext_noop ("delete record"), 0,
628      &GNUNET_GETOPT_set_one, &del},   
629     {'D', "display", NULL,
630      gettext_noop ("display records"), 0,
631      &GNUNET_GETOPT_set_one, &list},   
632     {'e', "expiration", "TIME",
633      gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"), 1,
634      &GNUNET_GETOPT_set_string, &expirationstring},   
635     {'n', "name", "NAME",
636      gettext_noop ("name of the record to add/delete/display"), 1,
637      &GNUNET_GETOPT_set_string, &name},   
638     {'t', "type", "TYPE",
639      gettext_noop ("type of the record to add/delete/display"), 1,
640      &GNUNET_GETOPT_set_string, &typestring},   
641     {'u', "uri", "URI",
642      gettext_noop ("URI to import into our zone"), 1,
643      &GNUNET_GETOPT_set_string, &uri},   
644     {'V', "value", "VALUE",
645      gettext_noop ("value of the record to add/delete"), 1,
646      &GNUNET_GETOPT_set_string, &value},   
647     {'p', "public", NULL,
648      gettext_noop ("create or list public record"), 0,
649      &GNUNET_GETOPT_set_one, &public},
650     {'N', "non-authority", NULL,
651      gettext_noop ("create or list non-authority record"), 0,
652      &GNUNET_GETOPT_set_one, &nonauthority},
653     {'z', "zonekey", "FILENAME",
654      gettext_noop ("filename with the zone key"), 1,
655      &GNUNET_GETOPT_set_string, &keyfile},   
656     GNUNET_GETOPT_OPTION_END
657   };
658
659   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
660     return 2;
661
662   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
663   if (GNUNET_OK !=
664       GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
665                           _("GNUnet zone manipulation tool"), 
666                           options,
667                           &run, NULL))
668     return 1;
669
670   return ret;
671 }
672
673 /* end of gnunet-namestore.c */