- test for namestore
[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 static void
321 testservice_task (void *cls,
322                   const struct GNUNET_SCHEDULER_TaskContext *tc)
323 {
324   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
325   uint32_t type;
326   void *data = NULL;
327   size_t data_size = 0;
328   struct GNUNET_TIME_Relative etime_rel;
329   struct GNUNET_TIME_Absolute etime_abs;
330   int etime_is_rel = GNUNET_SYSERR;
331   struct GNUNET_NAMESTORE_RecordData rd;
332
333   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
334
335   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
336   {
337       FPRINTF (stderr, _("Service `%s' is not running\n"), "namestore");
338       return;
339   }
340
341
342   if (NULL == keyfile)
343   {
344     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
345                                                               "ZONEKEY", &keyfile))
346     {
347       fprintf (stderr,
348                _("Option `%s' not given, but I need a zone key file!\n"),
349                "z");
350       return;
351     }
352     fprintf (stderr,
353              _("Using default zone file `%s'\n"),
354              keyfile);
355   }
356   zone_pkey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
357   GNUNET_free (keyfile);
358   keyfile = NULL;
359   if (! (add|del|list|(NULL != uri)))
360   {
361     /* nothing more to be done */  
362     fprintf (stderr,
363              _("No options given\n"));
364     GNUNET_CRYPTO_rsa_key_free (zone_pkey);
365     zone_pkey = NULL;
366     return; 
367   }
368   if (NULL == zone_pkey)
369   {
370     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371                 _("Failed to read or create private zone key\n"));
372     return;
373   }
374   GNUNET_CRYPTO_rsa_key_get_public (zone_pkey,
375                                     &pub);
376   GNUNET_CRYPTO_short_hash (&pub, sizeof (pub), &zone);
377
378   ns = GNUNET_NAMESTORE_connect (cfg);
379   if (NULL == ns)
380   {
381     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
382                 _("Failed to connect to namestore\n"));
383     return;
384   }
385   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
386                                 &do_shutdown, NULL);
387   if (NULL == typestring)
388     type = 0;
389   else
390     type = GNUNET_NAMESTORE_typename_to_number (typestring);
391   if (UINT32_MAX == type)
392   {
393     fprintf (stderr, _("Unsupported type `%s'\n"), typestring);
394     GNUNET_SCHEDULER_shutdown ();
395     ret = 1;
396     return;
397   }
398   if ((NULL == typestring) && (add | del))
399   {
400     fprintf (stderr,
401              _("Missing option `%s' for operation `%s'\n"),
402              "-t", _("add/del"));
403     GNUNET_SCHEDULER_shutdown ();
404     ret = 1;
405     return;     
406   }
407   if (NULL != value)
408   {
409     if (GNUNET_OK !=
410         GNUNET_NAMESTORE_string_to_value (type,
411                                           value,
412                                           &data,
413                                           &data_size))
414       {
415         fprintf (stderr, _("Value `%s' invalid for record type `%s'\n"),
416                  value,
417                  typestring);
418         GNUNET_SCHEDULER_shutdown ();
419         ret = 1;
420         return;
421       }
422   } else if (add | del)
423   {
424     fprintf (stderr,
425              _("Missing option `%s' for operation `%s'\n"),
426              "-V", _("add/del"));
427     ret = 1;   
428     GNUNET_SCHEDULER_shutdown ();
429     return;     
430   }
431   if (NULL != expirationstring)
432   {
433     if (0 == strcmp (expirationstring, "never"))
434     {
435       etime_abs = GNUNET_TIME_UNIT_FOREVER_ABS;
436       etime_is_rel = GNUNET_NO;
437     }
438     else if (GNUNET_OK ==
439              GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
440                                                     &etime_rel))
441     {
442       etime_is_rel = GNUNET_YES;
443     }
444     else if (GNUNET_OK == 
445              GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
446                                                     &etime_abs))
447     {
448       etime_is_rel = GNUNET_NO;
449     }
450     else
451     {
452       fprintf (stderr,
453                _("Invalid time format `%s'\n"),
454                expirationstring);
455       GNUNET_SCHEDULER_shutdown ();
456       ret = 1;
457       return;     
458     }
459     if (etime_is_rel && del)
460     {
461       fprintf (stderr,
462                _("Deletion requires either absolute time, or no time at all. Got relative time `%s' instead.\n"),
463                expirationstring);
464       GNUNET_SCHEDULER_shutdown ();
465       ret = 1;
466       return;
467     }
468   } 
469   else if (add)
470   {
471     fprintf (stderr,
472              _("Missing option `%s' for operation `%s'\n"),
473              "-e", _("add"));
474     GNUNET_SCHEDULER_shutdown ();
475     ret = 1;    
476     return;     
477   }
478   memset (&rd, 0, sizeof (rd));
479   if (add)
480   {
481     if (NULL == name)
482     {
483       fprintf (stderr,
484                _("Missing option `%s' for operation `%s'\n"),
485                "-n", _("add"));
486       GNUNET_SCHEDULER_shutdown ();
487       ret = 1;    
488       return;     
489     }
490     rd.data = data;
491     rd.data_size = data_size;
492     rd.record_type = type;
493     if (GNUNET_YES == etime_is_rel)
494     {
495       rd.expiration_time = etime_rel.rel_value;
496       rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
497     }
498     else if (GNUNET_NO == etime_is_rel)
499       rd.expiration_time = etime_abs.abs_value;
500     else
501     {
502       fprintf (stderr,
503                _("No valid expiration time for operation `%s'\n"),
504                _("add"));
505       GNUNET_SCHEDULER_shutdown ();
506       ret = 1;
507       return;
508     }
509     if (1 != nonauthority)
510       rd.flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
511     if (1 != public)
512       rd.flags |= GNUNET_NAMESTORE_RF_PRIVATE;
513     add_qe = GNUNET_NAMESTORE_record_create (ns,
514                                              zone_pkey,
515                                              name,
516                                              &rd,
517                                              &add_continuation,
518                                              &add_qe);
519   }
520   if (del)
521   {
522     if (NULL == name)
523     {
524       fprintf (stderr,
525                _("Missing option `%s' for operation `%s'\n"),
526                "-n", _("del"));
527       GNUNET_SCHEDULER_shutdown ();
528       ret = 1;
529       return;     
530     }
531     rd.data = data;
532     rd.data_size = data_size;
533     rd.record_type = type;
534     rd.expiration_time = 0;
535     if (!etime_is_rel)
536       rd.expiration_time = etime_abs.abs_value;
537     rd.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
538     del_qe = GNUNET_NAMESTORE_record_remove (ns,
539                                              zone_pkey,
540                                              name,
541                                              &rd,
542                                              &del_continuation,
543                                              NULL);
544   }
545   if (list)
546   {
547     uint32_t must_not_flags = 0;
548
549     if (1 == nonauthority) /* List non-authority records */
550       must_not_flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
551
552     if (1 == public)
553       must_not_flags |= GNUNET_NAMESTORE_RF_PRIVATE;
554
555     list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
556                                                      &zone,
557                                                      GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION,
558                                                      must_not_flags,
559                                                      &display_record,
560                                                      NULL);
561   }
562   if (NULL != uri)
563   {
564     char sh[53];
565     char name[64];
566     struct GNUNET_CRYPTO_ShortHashCode sc;
567
568     if ( (2 != (sscanf (uri,
569                         "gnunet://gns/%52s/%63s",
570                         sh,
571                         name)) ) ||
572          (GNUNET_OK !=
573           GNUNET_CRYPTO_short_hash_from_string (sh, &sc)) )
574     {
575       fprintf (stderr, 
576                _("Invalid URI `%s'\n"),
577                uri);
578       GNUNET_SCHEDULER_shutdown ();
579       ret = 1;
580       return;
581     }
582     rd.data = &sc;
583     rd.data_size = sizeof (struct GNUNET_CRYPTO_ShortHashCode);
584     rd.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
585     if (GNUNET_YES == etime_is_rel)
586     {
587       rd.expiration_time = etime_rel.rel_value;
588       rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
589     }
590     else if (GNUNET_NO == etime_is_rel)
591       rd.expiration_time = etime_abs.abs_value;
592     else    
593       rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value;
594     if (1 != nonauthority)
595       rd.flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
596
597     add_qe_uri = GNUNET_NAMESTORE_record_create (ns,
598                                                  zone_pkey,
599                                                  name,
600                                                  &rd,
601                                                  &add_continuation,
602                                                  &add_qe_uri);
603   }
604   GNUNET_free_non_null (data);
605 }
606
607
608 /**
609  * Main function that will be run.
610  *
611  * @param cls closure
612  * @param args remaining command-line arguments
613  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
614  * @param cfg configuration
615  */
616 static void
617 run (void *cls, char *const *args, const char *cfgfile,
618      const struct GNUNET_CONFIGURATION_Handle *cfg)
619 {
620
621   if ( (NULL != args[0]) && (NULL == uri) )
622     uri = GNUNET_strdup (args[0]);
623
624   GNUNET_CLIENT_service_test ("namestore", cfg,
625       GNUNET_TIME_UNIT_SECONDS,
626       &testservice_task,
627       (void *) cfg);
628 }
629
630
631 /**
632  * The main function for gnunet-namestore.
633  *
634  * @param argc number of arguments from the command line
635  * @param argv command line arguments
636  * @return 0 ok, 1 on error
637  */
638 int
639 main (int argc, char *const *argv)
640 {
641   nonauthority = -1;
642   public = -1;
643
644   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
645     {'a', "add", NULL,
646      gettext_noop ("add record"), 0,
647      &GNUNET_GETOPT_set_one, &add},
648     {'d', "delete", NULL,
649      gettext_noop ("delete record"), 0,
650      &GNUNET_GETOPT_set_one, &del},   
651     {'D', "display", NULL,
652      gettext_noop ("display records"), 0,
653      &GNUNET_GETOPT_set_one, &list},   
654     {'e', "expiration", "TIME",
655      gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"), 1,
656      &GNUNET_GETOPT_set_string, &expirationstring},   
657     {'n', "name", "NAME",
658      gettext_noop ("name of the record to add/delete/display"), 1,
659      &GNUNET_GETOPT_set_string, &name},   
660     {'t', "type", "TYPE",
661      gettext_noop ("type of the record to add/delete/display"), 1,
662      &GNUNET_GETOPT_set_string, &typestring},   
663     {'u', "uri", "URI",
664      gettext_noop ("URI to import into our zone"), 1,
665      &GNUNET_GETOPT_set_string, &uri},   
666     {'V', "value", "VALUE",
667      gettext_noop ("value of the record to add/delete"), 1,
668      &GNUNET_GETOPT_set_string, &value},   
669     {'p', "public", NULL,
670      gettext_noop ("create or list public record"), 0,
671      &GNUNET_GETOPT_set_one, &public},
672     {'N', "non-authority", NULL,
673      gettext_noop ("create or list non-authority record"), 0,
674      &GNUNET_GETOPT_set_one, &nonauthority},
675     {'z', "zonekey", "FILENAME",
676      gettext_noop ("filename with the zone key"), 1,
677      &GNUNET_GETOPT_set_string, &keyfile},   
678     GNUNET_GETOPT_OPTION_END
679   };
680
681   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
682     return 2;
683
684   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
685   if (GNUNET_OK !=
686       GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
687                           _("GNUnet zone manipulation tool"), 
688                           options,
689                           &run, NULL))
690     return 1;
691
692   return ret;
693 }
694
695 /* end of gnunet-namestore.c */