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