-cleaning up client api to test for service availability
[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_EccKeyGenerationContext * 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_EccPrivateKey *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_ecc_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_ecc_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_EccPublicKeyBinaryEncoded *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_EccSignature *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
330 /**
331  * Function called with the result of the ECC key generation.
332  *
333  * @param cls our configuration
334  * @param pk our private key, NULL on failure
335  * @param emsg NULL on success, otherwise error message
336  */
337 static void
338 key_generation_cb (void *cls,
339                    struct GNUNET_CRYPTO_EccPrivateKey *pk,
340                    const char *emsg)
341 {
342   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
343   struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pub;
344   uint32_t type;
345   void *data = NULL;
346   size_t data_size = 0;
347   struct GNUNET_TIME_Relative etime_rel;
348   struct GNUNET_TIME_Absolute etime_abs;
349   int etime_is_rel = GNUNET_SYSERR;
350   struct GNUNET_NAMESTORE_RecordData rd;
351
352   keygen = NULL;
353   if (NULL == pk)
354   {
355     GNUNET_SCHEDULER_shutdown ();
356     return;
357   }
358   zone_pkey = pk;
359
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_ecc_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_ecc_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 /**
611  * Function called with the result from the check if the namestore
612  * service is actually running.  If it is, we start the actual
613  * operation.
614  *
615  * @param cls closure with our configuration
616  * @param result GNUNET_YES if the namestore service is running
617  */
618 static void
619 testservice_task (void *cls,
620                   int result)
621 {
622   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
623
624   if (GNUNET_YES != result)
625   {
626     FPRINTF (stderr, _("Service `%s' is not running\n"), 
627              "namestore");
628     return;
629   }
630   if (NULL == keyfile)
631   {
632     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
633                                                               "ZONEKEY", &keyfile))
634     {
635       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
636                                  "gns", "ZONEKEY");
637       return;
638     }
639     fprintf (stderr,
640              _("Using default zone file `%s'\n"),
641              keyfile);
642   }
643   keygen = GNUNET_CRYPTO_ecc_key_create_start (keyfile,
644                                                &key_generation_cb, (void *) cfg);
645   GNUNET_free (keyfile);
646   keyfile = NULL;
647   if (NULL == keygen)
648   {
649     GNUNET_SCHEDULER_shutdown ();
650     ret = 1;
651   }
652 }
653
654
655 /**
656  * Main function that will be run.
657  *
658  * @param cls closure
659  * @param args remaining command-line arguments
660  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
661  * @param cfg configuration
662  */
663 static void
664 run (void *cls, char *const *args, const char *cfgfile,
665      const struct GNUNET_CONFIGURATION_Handle *cfg)
666 {
667
668   if ( (NULL != args[0]) && (NULL == uri) )
669     uri = GNUNET_strdup (args[0]);
670
671   GNUNET_CLIENT_service_test ("namestore", cfg,
672                               GNUNET_TIME_UNIT_SECONDS,
673                               &testservice_task,
674                               (void *) cfg);
675 }
676
677
678 /**
679  * The main function for gnunet-namestore.
680  *
681  * @param argc number of arguments from the command line
682  * @param argv command line arguments
683  * @return 0 ok, 1 on error
684  */
685 int
686 main (int argc, char *const *argv)
687 {
688   nonauthority = -1;
689   public = -1;
690
691   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
692     {'a', "add", NULL,
693      gettext_noop ("add record"), 0,
694      &GNUNET_GETOPT_set_one, &add},
695     {'d', "delete", NULL,
696      gettext_noop ("delete record"), 0,
697      &GNUNET_GETOPT_set_one, &del},   
698     {'D', "display", NULL,
699      gettext_noop ("display records"), 0,
700      &GNUNET_GETOPT_set_one, &list},   
701     {'e', "expiration", "TIME",
702      gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"), 1,
703      &GNUNET_GETOPT_set_string, &expirationstring},   
704     {'n', "name", "NAME",
705      gettext_noop ("name of the record to add/delete/display"), 1,
706      &GNUNET_GETOPT_set_string, &name},   
707     {'t', "type", "TYPE",
708      gettext_noop ("type of the record to add/delete/display"), 1,
709      &GNUNET_GETOPT_set_string, &typestring},   
710     {'u', "uri", "URI",
711      gettext_noop ("URI to import into our zone"), 1,
712      &GNUNET_GETOPT_set_string, &uri},   
713     {'V', "value", "VALUE",
714      gettext_noop ("value of the record to add/delete"), 1,
715      &GNUNET_GETOPT_set_string, &value},   
716     {'p', "public", NULL,
717      gettext_noop ("create or list public record"), 0,
718      &GNUNET_GETOPT_set_one, &public},
719     {'N', "non-authority", NULL,
720      gettext_noop ("create or list non-authority record"), 0,
721      &GNUNET_GETOPT_set_one, &nonauthority},
722     {'z', "zonekey", "FILENAME",
723      gettext_noop ("filename with the zone key"), 1,
724      &GNUNET_GETOPT_set_string, &keyfile},   
725     GNUNET_GETOPT_OPTION_END
726   };
727
728   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
729     return 2;
730
731   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
732   if (GNUNET_OK !=
733       GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
734                           _("GNUnet zone manipulation tool"), 
735                           options,
736                           &run, NULL))
737   {
738     GNUNET_free ((void*) argv);
739     return 1;
740   }
741   GNUNET_free ((void*) argv);
742   return ret;
743 }
744
745 /* end of gnunet-namestore.c */