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