-implement iteration over all zones (#3016)
[oweals/gnunet.git] / src / namestore / gnunet-namestore.c
1
2 /*
3      This file is part of GNUnet.
4      (C) 2012, 2013 Christian Grothoff (and other contributing authors)
5
6      GNUnet is free software; you can redistribute it and/or modify
7      it under the terms of the GNU General Public License as published
8      by the Free Software Foundation; either version 3, or (at your
9      option) any later version.
10
11      GNUnet is distributed in the hope that it will be useful, but
12      WITHOUT ANY WARRANTY; without even the implied warranty of
13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14      General Public License for more details.
15
16      You should have received a copy of the GNU General Public License
17      along with GNUnet; see the file COPYING.  If not, write to the
18      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19      Boston, MA 02111-1307, USA.
20 */
21 /**
22  * @file gnunet-namestore.c
23  * @brief command line tool to manipulate the local zone
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - allow users to set record options (not just 'RF_AUTHORITY')
28  * - test
29  * - add options to list/lookup individual records
30  */
31 #include "platform.h"
32 #include <gnunet_util_lib.h>
33 #include <gnunet_dnsparser_lib.h>
34 #include <gnunet_identity_service.h>
35 #include <gnunet_namestore_service.h>
36
37
38 /**
39  * Handle to the namestore.
40  */
41 static struct GNUNET_NAMESTORE_Handle *ns;
42
43 /**
44  * Private key for the our zone.
45  */
46 static struct GNUNET_CRYPTO_EccPrivateKey zone_pkey;
47
48 /**
49  * Handle to identity lookup.
50  */
51 static struct GNUNET_IDENTITY_EgoLookup *el;
52
53 /**
54  * Name of the ego controlling the zone.
55  */
56 static char *ego_name;  
57
58 /**
59  * Desired action is to add a record.
60  */
61 static int add;
62
63 /**
64  * Iterator for the 'add' operation.
65  */
66 static struct GNUNET_NAMESTORE_ZoneIterator *add_zit;
67
68 /**
69  * Queue entry for the 'add-uri' operation.
70  */
71 static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri;
72
73 /**
74  * Queue entry for the 'add' operation.
75  */
76 static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
77
78 /**
79  * Desired action is to list records.
80  */
81 static int list;
82
83 /**
84  * List iterator for the 'list' operation.
85  */
86 static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
87
88 /**
89  * Desired action is to remove a record.
90  */
91 static int del;
92
93 /**
94  * Is record public
95  */
96 static int public;
97
98 /**
99  * Is record authority
100  */
101 static int nonauthority;
102
103 /**
104  * Queue entry for the 'del' operation.
105  */
106 static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
107
108 /**
109  * Name of the records to add/list/remove.
110  */
111 static char *name;
112
113 /**
114  * Value of the record to add/remove.
115  */
116 static char *value;
117
118 /**
119  * URI to import.
120  */
121 static char *uri;
122
123 /**
124  * Type of the record to add/remove, NULL to remove all.
125  */
126 static char *typestring;
127
128 /**
129  * Desired expiration time.
130  */
131 static char *expirationstring;
132
133 /**
134  * Global return value
135  */
136 static int ret;
137
138 /**
139  * Type string converted to DNS type value.
140  */
141 static uint32_t type;
142
143 /**
144  * Value in binary format.
145  */
146 static void *data;
147
148 /**
149  * Number of bytes in 'data'.
150  */
151 static size_t data_size;
152
153 /**
154  * Expirationstring converted to relative time.
155  */
156 static struct GNUNET_TIME_Relative etime_rel;
157
158 /**
159  * Expirationstring converted to absolute time.
160  */
161 static struct GNUNET_TIME_Absolute etime_abs;
162
163 /**
164  * Is expiration time relative or absolute time?
165  */
166 static int etime_is_rel = GNUNET_SYSERR;
167
168 /**
169  * Monitor handle.
170  */
171 static struct GNUNET_NAMESTORE_ZoneMonitor *zm;
172
173 /**
174  * Enables monitor mode.
175  */
176 static int monitor;
177
178
179 /**
180  * Task run on shutdown.  Cleans up everything.
181  *
182  * @param cls unused
183  * @param tc scheduler context
184  */
185 static void
186 do_shutdown (void *cls,
187              const struct GNUNET_SCHEDULER_TaskContext *tc)
188 {
189   if (NULL != el)
190   {
191     GNUNET_IDENTITY_ego_lookup_cancel (el);
192     el = NULL;
193   }
194   if (NULL != list_it)
195   {
196     GNUNET_NAMESTORE_zone_iteration_stop (list_it);
197     list_it = NULL;
198   }
199   if (NULL != add_qe)
200   {
201     GNUNET_NAMESTORE_cancel (add_qe);
202     add_qe = NULL;
203   }
204   if (NULL != add_qe_uri)
205   {
206     GNUNET_NAMESTORE_cancel (add_qe_uri);
207     add_qe_uri = NULL;
208   }
209   if (NULL != del_qe)
210   {
211     GNUNET_NAMESTORE_cancel (del_qe);
212     del_qe = NULL;
213   }
214   if (NULL != ns)
215   {
216     GNUNET_NAMESTORE_disconnect (ns);
217     ns = NULL;
218   }
219   memset (&zone_pkey, 0, sizeof (zone_pkey));
220   if (NULL != uri)
221   {
222     GNUNET_free (uri);
223     uri = NULL;
224   }
225   if (NULL != zm)
226   {
227     GNUNET_NAMESTORE_zone_monitor_stop (zm);
228     zm = NULL;
229   }
230   if (NULL != data)
231   {
232     GNUNET_free (data);
233     data = NULL;
234   }
235 }
236
237
238 /**
239  * Continuation called to notify client about result of the
240  * operation.
241  *
242  * @param cls closure, location of the QueueEntry pointer to NULL out
243  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
244  *                GNUNET_NO if content was already there
245  *                GNUNET_YES (or other positive value) on success
246  * @param emsg NULL on success, otherwise an error message
247  */
248 static void
249 add_continuation (void *cls,
250                   int32_t success,
251                   const char *emsg)
252 {
253   struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
254
255   *qe = NULL;
256   if (GNUNET_YES != success)
257   {
258     fprintf (stderr,
259              _("Adding record failed: %s\n"),
260              (GNUNET_NO == success) ? "record exists" : emsg);
261     if (GNUNET_NO != success)
262       ret = 1;
263   }
264   if ( (NULL == add_qe) &&
265        (NULL == add_qe_uri) &&
266        (NULL == del_qe) &&
267        (NULL == list_it) )
268     GNUNET_SCHEDULER_shutdown ();
269 }
270
271
272 /**
273  * Continuation called to notify client about result of the
274  * operation.
275  *
276  * @param cls closure, unused
277  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
278  *                GNUNET_NO if content was already there
279  *                GNUNET_YES (or other positive value) on success
280  * @param emsg NULL on success, otherwise an error message
281  */
282 static void
283 del_continuation (void *cls,
284                   int32_t success,
285                   const char *emsg)
286 {
287   del_qe = NULL;
288   if (success != GNUNET_YES)
289     fprintf (stderr,
290              _("Deleting record failed: %s\n"),
291              emsg);
292   if ( (NULL == add_qe) &&
293        (NULL == add_qe_uri) &&
294        (NULL == list_it) )
295     GNUNET_SCHEDULER_shutdown ();
296 }
297
298
299 /**
300  * Process a record that was stored in the namestore.
301  *
302  * @param cls closure
303  * @param zone_key private key of the zone
304  * @param name name that is being mapped (at most 255 characters long)
305  * @param rd_len number of entries in 'rd' array
306  * @param rd array of records with data to store
307  */
308 static void
309 display_record (void *cls,
310                 const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
311                 const char *name,
312                 unsigned int rd_len,
313                 const struct GNUNET_NAMESTORE_RecordData *rd)
314 {
315   const char *typestring;
316   char *s;
317   unsigned int i;
318
319   if (NULL == name)
320   {
321     list_it = NULL;
322     if ( (NULL == del_qe) &&
323          (NULL == add_qe_uri) &&
324          (NULL == add_qe) )
325       GNUNET_SCHEDULER_shutdown ();
326     return;
327   }
328   FPRINTF (stdout,
329            "%s:\n",
330            name);
331   for (i=0;i<rd_len;i++)
332   {
333     typestring = GNUNET_NAMESTORE_number_to_typename (rd[i].record_type);
334     s = GNUNET_NAMESTORE_value_to_string (rd[i].record_type,
335                                           rd[i].data,
336                                           rd[i].data_size);
337     if (NULL == s)
338     {
339       FPRINTF (stdout, _("\tCorrupt or unsupported record of type %u\n"),
340                (unsigned int) rd[i].record_type);
341       continue;
342     }
343     FPRINTF (stdout, 
344              "\t%s: %s\n",
345              typestring, 
346              s);
347     GNUNET_free (s);    
348   }
349   FPRINTF (stdout, "%s", "\n");
350   GNUNET_NAMESTORE_zone_iterator_next (list_it);
351 }
352
353
354 /**
355  * Function called once we are in sync in monitor mode.
356  *
357  * @param cls NULL
358  */
359 static void
360 sync_cb (void *cls)
361 {
362   FPRINTF (stdout, "%s", "Monitor is now in sync.\n");
363 }
364
365
366 /**
367  * We're storing a record; this function is given the existing record
368  * so that we can merge the information.
369  *
370  * @param cls closure, unused
371  * @param zone_key private key of the zone
372  * @param rec_name name that is being mapped (at most 255 characters long)
373  * @param rd_count number of entries in 'rd' array
374  * @param rd array of records with data to store
375  */
376 static void
377 get_existing_record (void *cls,
378                      const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
379                      const char *rec_name,
380                      unsigned int rd_count,
381                      const struct GNUNET_NAMESTORE_RecordData *rd)
382 {
383   struct GNUNET_NAMESTORE_RecordData rdn[rd_count + 1];
384   struct GNUNET_NAMESTORE_RecordData *rde;
385
386   if ( (NULL != zone_key) &&
387        (0 != strcmp (rec_name, name)) )
388   {
389     GNUNET_NAMESTORE_zone_iterator_next (add_zit);
390     return;
391   }
392   memset (rdn, 0, sizeof (struct GNUNET_NAMESTORE_RecordData));
393   memcpy (&rdn[1], rd, rd_count * sizeof (struct GNUNET_NAMESTORE_RecordData));
394   /* FIXME: should add some logic to overwrite records if there
395      can only be one record of a particular type, and to check
396      if the combination of records is valid to begin with... */
397   rde = &rdn[0];
398   rde->data = data;
399   rde->data_size = data_size;
400   rde->record_type = type;
401   if (1 != nonauthority)
402     rde->flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
403   if (1 != public)
404     rde->flags |= GNUNET_NAMESTORE_RF_PRIVATE;
405   GNUNET_assert (NULL != name);
406   add_qe = GNUNET_NAMESTORE_records_store (ns,
407                                            &zone_pkey,
408                                            name,
409                                            rd_count + 1,
410                                            rde,
411                                            &add_continuation,
412                                            &add_qe);
413   GNUNET_NAMESTORE_zone_iteration_stop (add_zit);
414   add_zit = NULL;
415 }
416
417
418 /**
419  * Function called with the result from the check if the namestore
420  * service is actually running.  If it is, we start the actual
421  * operation.
422  *
423  * @param cls closure with our configuration
424  * @param result GNUNET_YES if the namestore service is running
425  */
426 static void
427 testservice_task (void *cls,
428                   int result)
429 {
430   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
431   struct GNUNET_CRYPTO_EccPublicKey pub;
432   struct GNUNET_NAMESTORE_RecordData rd;
433
434   if (GNUNET_YES != result)
435   {
436     FPRINTF (stderr, _("Service `%s' is not running\n"), 
437              "namestore");
438     return;
439   }
440   if (! (add|del|list|(NULL != uri)))
441   {
442     /* nothing more to be done */  
443     fprintf (stderr,
444              _("No options given\n"));
445     return; 
446   }
447   GNUNET_CRYPTO_ecc_key_get_public (&zone_pkey,
448                                     &pub);
449
450   ns = GNUNET_NAMESTORE_connect (cfg);
451   if (NULL == ns)
452   {
453     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454                 _("Failed to connect to namestore\n"));
455     return;
456   }
457   if (add)
458   {
459     if (NULL == name)
460     {
461       fprintf (stderr,
462                _("Missing option `%s' for operation `%s'\n"),
463                "-n", _("add"));
464       GNUNET_SCHEDULER_shutdown ();
465       ret = 1;    
466       return;     
467     }
468     if (NULL == typestring)
469     {
470       fprintf (stderr,
471                _("Missing option `%s' for operation `%s'\n"),
472                "-t", _("add"));
473       GNUNET_SCHEDULER_shutdown ();
474       ret = 1;
475       return;     
476     }
477     type = GNUNET_NAMESTORE_typename_to_number (typestring);
478     if (UINT32_MAX == type)
479     {
480       fprintf (stderr, _("Unsupported type `%s'\n"), typestring);
481       GNUNET_SCHEDULER_shutdown ();
482       ret = 1;
483       return;
484     }
485     if (NULL == value)
486     {
487       fprintf (stderr,
488                _("Missing option `%s' for operation `%s'\n"),
489                "-V", _("add"));
490       ret = 1;   
491       GNUNET_SCHEDULER_shutdown ();
492       return;     
493     }
494     if (GNUNET_OK !=
495         GNUNET_NAMESTORE_string_to_value (type,
496                                           value,
497                                           &data,
498                                           &data_size))
499     {
500       fprintf (stderr, _("Value `%s' invalid for record type `%s'\n"),
501                value,
502                typestring);
503       GNUNET_SCHEDULER_shutdown ();
504       ret = 1;
505       return;
506     }
507     if (NULL == expirationstring)
508     {
509       fprintf (stderr,
510                _("Missing option `%s' for operation `%s'\n"),
511                "-e", _("add"));
512       GNUNET_SCHEDULER_shutdown ();
513       ret = 1;    
514       return;     
515     }
516     if (0 == strcmp (expirationstring, "never"))
517     {
518       etime_abs = GNUNET_TIME_UNIT_FOREVER_ABS;
519       etime_is_rel = GNUNET_NO;
520     }
521     else if (GNUNET_OK ==
522              GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
523                                                     &etime_rel))
524     {
525       etime_is_rel = GNUNET_YES;
526     }
527     else if (GNUNET_OK == 
528              GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
529                                                     &etime_abs))
530     {
531       etime_is_rel = GNUNET_NO;
532     }
533     else
534     {
535       fprintf (stderr,
536                _("Invalid time format `%s'\n"),
537                expirationstring);
538       GNUNET_SCHEDULER_shutdown ();
539       ret = 1;
540       return;     
541     }
542     add_zit = GNUNET_NAMESTORE_zone_iteration_start (ns,
543                                                      &zone_pkey,
544                                                      &get_existing_record,
545                                                      NULL);
546   }
547   if (del)
548   {
549     if (NULL == name)
550     {
551       fprintf (stderr,
552                _("Missing option `%s' for operation `%s'\n"),
553                "-n", _("del"));
554       GNUNET_SCHEDULER_shutdown ();
555       ret = 1;
556       return;     
557     }
558     del_qe = GNUNET_NAMESTORE_records_store (ns,
559                                              &zone_pkey,
560                                              name,
561                                              0, NULL,
562                                              &del_continuation,
563                                              NULL);
564   }
565   if (list)
566   {
567     list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
568                                                      &zone_pkey,
569                                                      &display_record,
570                                                      NULL);
571   }
572   if (NULL != uri)
573   {
574     char sh[105];
575     char sname[64];
576     struct GNUNET_CRYPTO_EccPublicKey pkey;
577
578     if ( (2 != (sscanf (uri,
579                         "gnunet://gns/%104s/%63s",
580                         sh,
581                         sname)) ) ||
582          (GNUNET_OK !=
583           GNUNET_CRYPTO_ecc_public_key_from_string (sh, strlen (sh), &pkey)) )
584     {
585       fprintf (stderr, 
586                _("Invalid URI `%s'\n"),
587                uri);
588       GNUNET_SCHEDULER_shutdown ();
589       ret = 1;
590       return;
591     }
592     memset (&rd, 0, sizeof (rd));
593     rd.data = &pkey;
594     rd.data_size = sizeof (struct GNUNET_CRYPTO_EccPublicKey);
595     rd.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
596     if (GNUNET_YES == etime_is_rel)
597     {
598       rd.expiration_time = etime_rel.rel_value_us;
599       rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
600     }
601     else if (GNUNET_NO == etime_is_rel)
602       rd.expiration_time = etime_abs.abs_value_us;
603     else    
604       rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
605     if (1 != nonauthority)
606       rd.flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
607     add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
608                                                  &zone_pkey,
609                                                  sname,
610                                                  1,
611                                                  &rd,
612                                                  &add_continuation,
613                                                  &add_qe_uri);
614   }
615   if (monitor)
616   {
617     zm = GNUNET_NAMESTORE_zone_monitor_start (cfg,
618                                               &zone_pkey,
619                                               &display_record,
620                                               &sync_cb,
621                                               NULL);
622   }
623 }
624
625
626 /**
627  * Callback invoked from identity service with ego information.
628  * An @a ego of NULL means the ego was not found.
629  *
630  * @param cls closure with the configuration
631  * @param ego an ego known to identity service, or NULL 
632  */
633 static void
634 identity_cb (void *cls,
635              const struct GNUNET_IDENTITY_Ego *ego)
636 {
637   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
638
639   el = NULL;
640   if (NULL == ego)
641   {
642     fprintf (stderr, 
643              _("Ego `%s' not known to identity service\n"),
644              ego_name);
645     return;
646   }
647   zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
648   GNUNET_free (ego_name);
649   ego_name = NULL;
650   GNUNET_CLIENT_service_test ("namestore", cfg,
651                               GNUNET_TIME_UNIT_SECONDS,
652                               &testservice_task,
653                               (void *) cfg);
654 }
655
656
657 /**
658  * Main function that will be run.
659  *
660  * @param cls closure
661  * @param args remaining command-line arguments
662  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
663  * @param cfg configuration
664  */
665 static void
666 run (void *cls, char *const *args, const char *cfgfile,
667      const struct GNUNET_CONFIGURATION_Handle *cfg)
668 {
669   if (NULL == ego_name)
670   {
671     fprintf (stderr, 
672              _("You must specify which zone should be accessed\n"));
673     return;
674   }
675   if ( (NULL != args[0]) && (NULL == uri) )
676     uri = GNUNET_strdup (args[0]);
677   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
678                                 &do_shutdown, NULL);
679   el = GNUNET_IDENTITY_ego_lookup (cfg,
680                                    ego_name,
681                                    &identity_cb,
682                                    (void *) cfg);
683 }
684
685
686 /**
687  * The main function for gnunet-namestore.
688  *
689  * @param argc number of arguments from the command line
690  * @param argv command line arguments
691  * @return 0 ok, 1 on error
692  */
693 int
694 main (int argc, char *const *argv)
695 {
696   nonauthority = -1;
697   public = -1;
698
699   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
700     {'a', "add", NULL,
701      gettext_noop ("add record"), 0,
702      &GNUNET_GETOPT_set_one, &add},
703     {'d', "delete", NULL,
704      gettext_noop ("delete record"), 0,
705      &GNUNET_GETOPT_set_one, &del},   
706     {'D', "display", NULL,
707      gettext_noop ("display records"), 0,
708      &GNUNET_GETOPT_set_one, &list},   
709     {'e', "expiration", "TIME",
710      gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"), 1,
711      &GNUNET_GETOPT_set_string, &expirationstring},   
712     {'m', "monitor", NULL,
713      gettext_noop ("monitor changes in the namestore"), 0,
714      &GNUNET_GETOPT_set_one, &monitor},   
715     {'n', "name", "NAME",
716      gettext_noop ("name of the record to add/delete/display"), 1,
717      &GNUNET_GETOPT_set_string, &name},   
718     {'t', "type", "TYPE",
719      gettext_noop ("type of the record to add/delete/display"), 1,
720      &GNUNET_GETOPT_set_string, &typestring},   
721     {'u', "uri", "URI",
722      gettext_noop ("URI to import into our zone"), 1,
723      &GNUNET_GETOPT_set_string, &uri},   
724     {'V', "value", "VALUE",
725      gettext_noop ("value of the record to add/delete"), 1,
726      &GNUNET_GETOPT_set_string, &value},   
727     {'p', "public", NULL,
728      gettext_noop ("create or list public record"), 0,
729      &GNUNET_GETOPT_set_one, &public},
730     {'N', "non-authority", NULL,
731      gettext_noop ("create or list non-authority record"), 0,
732      &GNUNET_GETOPT_set_one, &nonauthority},
733     {'z', "zone", "EGO",
734      gettext_noop ("name of the ego controlling the zone"), 1,
735      &GNUNET_GETOPT_set_string, &ego_name},   
736     GNUNET_GETOPT_OPTION_END
737   };
738
739   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
740     return 2;
741
742   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
743   if (GNUNET_OK !=
744       GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
745                           _("GNUnet zone manipulation tool"), 
746                           options,
747                           &run, NULL))
748   {
749     GNUNET_free ((void*) argv);
750     return 1;
751   }
752   GNUNET_free ((void*) argv);
753   return ret;
754 }
755
756 /* end of gnunet-namestore.c */