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