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