-fix crash in gnunet-namestore -a if no other records already existed
[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  * - 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 lookup.
49  */
50 static struct GNUNET_IDENTITY_EgoLookup *el;
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 (opposite of #GNUNET_NAMESTORE_RF_PRIVATE)
94  */
95 static int public;
96
97 /**
98  * Is record a shadow record (#GNUNET_NAMESTORE_RF_SHADOW_RECORD)
99  */
100 static int shadow;
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 != el)
189   {
190     GNUNET_IDENTITY_ego_lookup_cancel (el);
191     el = NULL;
192   }
193   if (NULL != list_it)
194   {
195     GNUNET_NAMESTORE_zone_iteration_stop (list_it);
196     list_it = NULL;
197   }
198   if (NULL != add_qe)
199   {
200     GNUNET_NAMESTORE_cancel (add_qe);
201     add_qe = NULL;
202   }
203   if (NULL != add_qe_uri)
204   {
205     GNUNET_NAMESTORE_cancel (add_qe_uri);
206     add_qe_uri = NULL;
207   }
208   if (NULL != del_qe)
209   {
210     GNUNET_NAMESTORE_cancel (del_qe);
211     del_qe = NULL;
212   }
213   if (NULL != ns)
214   {
215     GNUNET_NAMESTORE_disconnect (ns);
216     ns = NULL;
217   }
218   memset (&zone_pkey, 0, sizeof (zone_pkey));
219   if (NULL != uri)
220   {
221     GNUNET_free (uri);
222     uri = NULL;
223   }
224   if (NULL != zm)
225   {
226     GNUNET_NAMESTORE_zone_monitor_stop (zm);
227     zm = NULL;
228   }
229   if (NULL != data)
230   {
231     GNUNET_free (data);
232     data = NULL;
233   }
234 }
235
236
237 /**
238  * Continuation called to notify client about result of the
239  * operation.
240  *
241  * @param cls closure, location of the QueueEntry pointer to NULL out
242  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
243  *                #GNUNET_NO if content was already there
244  *                #GNUNET_YES (or other positive value) on success
245  * @param emsg NULL on success, otherwise an error message
246  */
247 static void
248 add_continuation (void *cls,
249                   int32_t success,
250                   const char *emsg)
251 {
252   struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
253
254   *qe = NULL;
255   if (GNUNET_YES != success)
256   {
257     fprintf (stderr,
258              _("Adding record failed: %s\n"),
259              (GNUNET_NO == success) ? "record exists" : emsg);
260     if (GNUNET_NO != success)
261       ret = 1;
262   }
263   if ( (NULL == add_qe) &&
264        (NULL == add_qe_uri) &&
265        (NULL == del_qe) &&
266        (NULL == list_it) )
267     GNUNET_SCHEDULER_shutdown ();
268 }
269
270
271 /**
272  * Continuation called to notify client about result of the
273  * operation.
274  *
275  * @param cls closure, unused
276  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
277  *                #GNUNET_NO if content was already there
278  *                #GNUNET_YES (or other positive value) on success
279  * @param emsg NULL on success, otherwise an error message
280  */
281 static void
282 del_continuation (void *cls,
283                   int32_t success,
284                   const char *emsg)
285 {
286   del_qe = NULL;
287   if (success != GNUNET_YES)
288     fprintf (stderr,
289              _("Deleting record failed: %s\n"),
290              emsg);
291   if ( (NULL == add_qe) &&
292        (NULL == add_qe_uri) &&
293        (NULL == list_it) )
294     GNUNET_SCHEDULER_shutdown ();
295 }
296
297
298 /**
299  * Process a record that was stored in the namestore.
300  *
301  * @param cls closure
302  * @param zone_key private key of the zone
303  * @param name name that is being mapped (at most 255 characters long)
304  * @param rd_len number of entries in @a rd array
305  * @param rd array of records with data to store
306  */
307 static void
308 display_record (void *cls,
309                 const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
310                 const char *name,
311                 unsigned int rd_len,
312                 const struct GNUNET_NAMESTORE_RecordData *rd)
313 {
314   const char *typestring;
315   char *s;
316   unsigned int i;
317
318   if (NULL == name)
319   {
320     list_it = NULL;
321     if ( (NULL == del_qe) &&
322          (NULL == add_qe_uri) &&
323          (NULL == add_qe) )    
324       GNUNET_SCHEDULER_shutdown ();
325     return;
326   }
327   FPRINTF (stdout,
328            "%s:\n",
329            name);
330   for (i=0;i<rd_len;i++)
331   {
332     typestring = GNUNET_NAMESTORE_number_to_typename (rd[i].record_type);
333     s = GNUNET_NAMESTORE_value_to_string (rd[i].record_type,
334                                           rd[i].data,
335                                           rd[i].data_size);
336     if (NULL == s)
337     {
338       FPRINTF (stdout, _("\tCorrupt or unsupported record of type %u\n"),
339                (unsigned int) rd[i].record_type);
340       continue;
341     }
342     FPRINTF (stdout, 
343              "\t%s: %s\n",
344              typestring, 
345              s);
346     GNUNET_free (s);    
347   }
348   FPRINTF (stdout, "%s", "\n");
349   GNUNET_NAMESTORE_zone_iterator_next (list_it);
350 }
351
352
353 /**
354  * Function called once we are in sync in monitor mode.
355  *
356  * @param cls NULL
357  */
358 static void
359 sync_cb (void *cls)
360 {
361   FPRINTF (stdout, "%s", "Monitor is now in sync.\n");
362 }
363
364
365 /**
366  * We're storing a record; this function is given the existing record
367  * so that we can merge the information.
368  *
369  * @param cls closure, unused
370  * @param zone_key private key of the zone
371  * @param rec_name name that is being mapped (at most 255 characters long)
372  * @param rd_count number of entries in @a rd array
373  * @param rd array of records with data to store
374  */
375 static void
376 get_existing_record (void *cls,
377                      const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
378                      const char *rec_name,
379                      unsigned int rd_count,
380                      const struct GNUNET_NAMESTORE_RecordData *rd)
381 {
382   struct GNUNET_NAMESTORE_RecordData rdn[rd_count + 1];
383   struct GNUNET_NAMESTORE_RecordData *rde;
384
385   if ( (NULL != zone_key) &&
386        (0 != strcmp (rec_name, name)) )
387   {
388     GNUNET_NAMESTORE_zone_iterator_next (add_zit);
389     return;
390   }
391   memset (rdn, 0, sizeof (struct GNUNET_NAMESTORE_RecordData));
392   memcpy (&rdn[1], rd, rd_count * sizeof (struct GNUNET_NAMESTORE_RecordData));
393   /* FIXME: should add some logic to overwrite records if there
394      can only be one record of a particular type, and to check
395      if the combination of records is valid to begin with... */
396   rde = &rdn[0];
397   rde->data = data;
398   rde->data_size = data_size;
399   rde->record_type = type;
400   if (1 != shadow)
401     rde->flags |= GNUNET_NAMESTORE_RF_SHADOW_RECORD;
402   if (1 != public)
403     rde->flags |= GNUNET_NAMESTORE_RF_PRIVATE;
404   GNUNET_assert (NULL != name);
405   add_qe = GNUNET_NAMESTORE_records_store (ns,
406                                            &zone_pkey,
407                                            name,
408                                            rd_count + 1,
409                                            rde,
410                                            &add_continuation,
411                                            &add_qe);
412   /* only cancel if we were not told that this
413      was the end of the iteration already */
414   if (NULL != rec_name)
415     GNUNET_NAMESTORE_zone_iteration_stop (add_zit);
416   add_zit = NULL;
417 }
418
419
420 /**
421  * Function called with the result from the check if the namestore
422  * service is actually running.  If it is, we start the actual
423  * operation.
424  *
425  * @param cls closure with our configuration
426  * @param result #GNUNET_YES if the namestore service is running
427  */
428 static void
429 testservice_task (void *cls,
430                   int result)
431 {
432   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
433   struct GNUNET_CRYPTO_EccPublicKey pub;
434   struct GNUNET_NAMESTORE_RecordData rd;
435
436   if (GNUNET_YES != result)
437   {
438     FPRINTF (stderr, _("Service `%s' is not running\n"), 
439              "namestore");
440     return;
441   }
442   if (! (add|del|list|(NULL != uri)))
443   {
444     /* nothing more to be done */  
445     fprintf (stderr,
446              _("No options given\n"));
447     return; 
448   }
449   GNUNET_CRYPTO_ecc_key_get_public (&zone_pkey,
450                                     &pub);
451
452   ns = GNUNET_NAMESTORE_connect (cfg);
453   if (NULL == ns)
454   {
455     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
456                 _("Failed to connect to namestore\n"));
457     return;
458   }
459   if (add)
460   {
461     if (NULL == name)
462     {
463       fprintf (stderr,
464                _("Missing option `%s' for operation `%s'\n"),
465                "-n", _("add"));
466       GNUNET_SCHEDULER_shutdown ();
467       ret = 1;    
468       return;     
469     }
470     if (NULL == typestring)
471     {
472       fprintf (stderr,
473                _("Missing option `%s' for operation `%s'\n"),
474                "-t", _("add"));
475       GNUNET_SCHEDULER_shutdown ();
476       ret = 1;
477       return;     
478     }
479     type = GNUNET_NAMESTORE_typename_to_number (typestring);
480     if (UINT32_MAX == type)
481     {
482       fprintf (stderr, _("Unsupported type `%s'\n"), typestring);
483       GNUNET_SCHEDULER_shutdown ();
484       ret = 1;
485       return;
486     }
487     if (NULL == value)
488     {
489       fprintf (stderr,
490                _("Missing option `%s' for operation `%s'\n"),
491                "-V", _("add"));
492       ret = 1;   
493       GNUNET_SCHEDULER_shutdown ();
494       return;     
495     }
496     if (GNUNET_OK !=
497         GNUNET_NAMESTORE_string_to_value (type,
498                                           value,
499                                           &data,
500                                           &data_size))
501     {
502       fprintf (stderr, _("Value `%s' invalid for record type `%s'\n"),
503                value,
504                typestring);
505       GNUNET_SCHEDULER_shutdown ();
506       ret = 1;
507       return;
508     }
509     if (NULL == expirationstring)
510     {
511       fprintf (stderr,
512                _("Missing option `%s' for operation `%s'\n"),
513                "-e", _("add"));
514       GNUNET_SCHEDULER_shutdown ();
515       ret = 1;    
516       return;     
517     }
518     if (0 == strcmp (expirationstring, "never"))
519     {
520       etime_abs = GNUNET_TIME_UNIT_FOREVER_ABS;
521       etime_is_rel = GNUNET_NO;
522     }
523     else if (GNUNET_OK ==
524              GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
525                                                     &etime_rel))
526     {
527       etime_is_rel = GNUNET_YES;
528     }
529     else if (GNUNET_OK == 
530              GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
531                                                     &etime_abs))
532     {
533       etime_is_rel = GNUNET_NO;
534     }
535     else
536     {
537       fprintf (stderr,
538                _("Invalid time format `%s'\n"),
539                expirationstring);
540       GNUNET_SCHEDULER_shutdown ();
541       ret = 1;
542       return;     
543     }
544     add_zit = GNUNET_NAMESTORE_zone_iteration_start (ns,
545                                                      &zone_pkey,
546                                                      &get_existing_record,
547                                                      NULL);
548   }
549   if (del)
550   {
551     if (NULL == name)
552     {
553       fprintf (stderr,
554                _("Missing option `%s' for operation `%s'\n"),
555                "-n", _("del"));
556       GNUNET_SCHEDULER_shutdown ();
557       ret = 1;
558       return;     
559     }
560     del_qe = GNUNET_NAMESTORE_records_store (ns,
561                                              &zone_pkey,
562                                              name,
563                                              0, NULL,
564                                              &del_continuation,
565                                              NULL);
566   }
567   if (list)
568   {
569     list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
570                                                      &zone_pkey,
571                                                      &display_record,
572                                                      NULL);
573   }
574   if (NULL != uri)
575   {
576     char sh[105];
577     char sname[64];
578     struct GNUNET_CRYPTO_EccPublicKey pkey;
579
580     if ( (2 != (sscanf (uri,
581                         "gnunet://gns/%104s/%63s",
582                         sh,
583                         sname)) ) ||
584          (GNUNET_OK !=
585           GNUNET_CRYPTO_ecc_public_key_from_string (sh, strlen (sh), &pkey)) )
586     {
587       fprintf (stderr, 
588                _("Invalid URI `%s'\n"),
589                uri);
590       GNUNET_SCHEDULER_shutdown ();
591       ret = 1;
592       return;
593     }
594     memset (&rd, 0, sizeof (rd));
595     rd.data = &pkey;
596     rd.data_size = sizeof (struct GNUNET_CRYPTO_EccPublicKey);
597     rd.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
598     if (GNUNET_YES == etime_is_rel)
599     {
600       rd.expiration_time = etime_rel.rel_value_us;
601       rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
602     }
603     else if (GNUNET_NO == etime_is_rel)
604       rd.expiration_time = etime_abs.abs_value_us;
605     else    
606       rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
607     if (1 != shadow)
608       rd.flags |= GNUNET_NAMESTORE_RF_SHADOW_RECORD;
609     add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
610                                                  &zone_pkey,
611                                                  sname,
612                                                  1,
613                                                  &rd,
614                                                  &add_continuation,
615                                                  &add_qe_uri);
616   }
617   if (monitor)
618   {
619     zm = GNUNET_NAMESTORE_zone_monitor_start (cfg,
620                                               &zone_pkey,
621                                               &display_record,
622                                               &sync_cb,
623                                               NULL);
624   }
625 }
626
627
628 /**
629  * Callback invoked from identity service with ego information.
630  * An @a ego of NULL means the ego was not found.
631  *
632  * @param cls closure with the configuration
633  * @param ego an ego known to identity service, or NULL 
634  */
635 static void
636 identity_cb (void *cls,
637              const struct GNUNET_IDENTITY_Ego *ego)
638 {
639   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
640
641   el = NULL;
642   if (NULL == ego)
643   {
644     fprintf (stderr, 
645              _("Ego `%s' not known to identity service\n"),
646              ego_name);
647     return;
648   }
649   zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
650   GNUNET_free (ego_name);
651   ego_name = NULL;
652   GNUNET_CLIENT_service_test ("namestore", cfg,
653                               GNUNET_TIME_UNIT_SECONDS,
654                               &testservice_task,
655                               (void *) cfg);
656 }
657
658
659 /**
660  * Main function that will be run.
661  *
662  * @param cls closure
663  * @param args remaining command-line arguments
664  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
665  * @param cfg configuration
666  */
667 static void
668 run (void *cls, char *const *args, const char *cfgfile,
669      const struct GNUNET_CONFIGURATION_Handle *cfg)
670 {
671   if (NULL == ego_name)
672   {
673     fprintf (stderr, 
674              _("You must specify which zone should be accessed\n"));
675     return;
676   }
677   if ( (NULL != args[0]) && (NULL == uri) )
678     uri = GNUNET_strdup (args[0]);
679   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
680                                 &do_shutdown, NULL);
681   el = GNUNET_IDENTITY_ego_lookup (cfg,
682                                    ego_name,
683                                    &identity_cb,
684                                    (void *) cfg);
685 }
686
687
688 /**
689  * The main function for gnunet-namestore.
690  *
691  * @param argc number of arguments from the command line
692  * @param argv command line arguments
693  * @return 0 ok, 1 on error
694  */
695 int
696 main (int argc, char *const *argv)
697 {
698   public = -1;
699
700   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
701     {'a', "add", NULL,
702      gettext_noop ("add record"), 0,
703      &GNUNET_GETOPT_set_one, &add},
704     {'d', "delete", NULL,
705      gettext_noop ("delete record"), 0,
706      &GNUNET_GETOPT_set_one, &del},   
707     {'D', "display", NULL,
708      gettext_noop ("display records"), 0,
709      &GNUNET_GETOPT_set_one, &list},   
710     {'e', "expiration", "TIME",
711      gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"), 1,
712      &GNUNET_GETOPT_set_string, &expirationstring},   
713     {'m', "monitor", NULL,
714      gettext_noop ("monitor changes in the namestore"), 0,
715      &GNUNET_GETOPT_set_one, &monitor},   
716     {'n', "name", "NAME",
717      gettext_noop ("name of the record to add/delete/display"), 1,
718      &GNUNET_GETOPT_set_string, &name},   
719     {'t', "type", "TYPE",
720      gettext_noop ("type of the record to add/delete/display"), 1,
721      &GNUNET_GETOPT_set_string, &typestring},   
722     {'u', "uri", "URI",
723      gettext_noop ("URI to import into our zone"), 1,
724      &GNUNET_GETOPT_set_string, &uri},   
725     {'V', "value", "VALUE",
726      gettext_noop ("value of the record to add/delete"), 1,
727      &GNUNET_GETOPT_set_string, &value},   
728     {'p', "public", NULL,
729      gettext_noop ("create or list public record"), 0,
730      &GNUNET_GETOPT_set_one, &public},
731     {'s', "shadow", NULL,
732      gettext_noop ("create shadow record (only valid if all other records of the same type have expired"), 0,
733      &GNUNET_GETOPT_set_one, &shadow},
734     {'z', "zone", "EGO",
735      gettext_noop ("name of the ego controlling the zone"), 1,
736      &GNUNET_GETOPT_set_string, &ego_name},   
737     GNUNET_GETOPT_OPTION_END
738   };
739
740   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
741     return 2;
742
743   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
744   if (GNUNET_OK !=
745       GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
746                           _("GNUnet zone manipulation tool"), 
747                           options,
748                           &run, NULL))
749   {
750     GNUNET_free ((void*) argv);
751     return 1;
752   }
753   GNUNET_free ((void*) argv);
754   return ret;
755 }
756
757 /* end of gnunet-namestore.c */