replace zone iteration with record lookup
[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  */
29 #include "platform.h"
30 #include <gnunet_util_lib.h>
31 #include <gnunet_dnsparser_lib.h>
32 #include <gnunet_identity_service.h>
33 #include <gnunet_gnsrecord_lib.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_EcdsaPrivateKey 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  * Queue entry for the 'add-uri' operation.
64  */
65 static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri;
66
67 /**
68  * Queue entry for the 'add' operation.
69  */
70 static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
71
72 /**
73  * Queue entry for the 'reverse lookup' operation (in combination with a name).
74  */
75 static struct GNUNET_NAMESTORE_QueueEntry *reverse_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_GNSRECORD_RF_PRIVATE)
94  */
95 static int public;
96
97 /**
98  * Is record a shadow record (#GNUNET_GNSRECORD_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  * Reverse lookup to perform.
124  */
125 static char *reverse_pkey;
126
127 /**
128  * Type of the record to add/remove, NULL to remove all.
129  */
130 static char *typestring;
131
132 /**
133  * Desired expiration time.
134  */
135 static char *expirationstring;
136
137 /**
138  * Global return value
139  */
140 static int ret;
141
142 /**
143  * Type string converted to DNS type value.
144  */
145 static uint32_t type;
146
147 /**
148  * Value in binary format.
149  */
150 static void *data;
151
152 /**
153  * Number of bytes in 'data'.
154  */
155 static size_t data_size;
156
157 /**
158  * Expirationstring converted to relative time.
159  */
160 static struct GNUNET_TIME_Relative etime_rel;
161
162 /**
163  * Expirationstring converted to absolute time.
164  */
165 static struct GNUNET_TIME_Absolute etime_abs;
166
167 /**
168  * Is expiration time relative or absolute time?
169  */
170 static int etime_is_rel = GNUNET_SYSERR;
171
172 /**
173  * Monitor handle.
174  */
175 static struct GNUNET_NAMESTORE_ZoneMonitor *zm;
176
177 /**
178  * Enables monitor mode.
179  */
180 static int monitor;
181
182
183 /**
184  * Task run on shutdown.  Cleans up everything.
185  *
186  * @param cls unused
187  * @param tc scheduler context
188  */
189 static void
190 do_shutdown (void *cls,
191              const struct GNUNET_SCHEDULER_TaskContext *tc)
192 {
193   if (NULL != el)
194   {
195     GNUNET_IDENTITY_ego_lookup_cancel (el);
196     el = NULL;
197   }
198   if (NULL != list_it)
199   {
200     GNUNET_NAMESTORE_zone_iteration_stop (list_it);
201     list_it = NULL;
202   }
203   if (NULL != add_qe)
204   {
205     GNUNET_NAMESTORE_cancel (add_qe);
206     add_qe = NULL;
207   }
208   if (NULL != add_qe_uri)
209   {
210     GNUNET_NAMESTORE_cancel (add_qe_uri);
211     add_qe_uri = NULL;
212   }
213   if (NULL != del_qe)
214   {
215     GNUNET_NAMESTORE_cancel (del_qe);
216     del_qe = NULL;
217   }
218   if (NULL != ns)
219   {
220     GNUNET_NAMESTORE_disconnect (ns);
221     ns = NULL;
222   }
223   memset (&zone_pkey, 0, sizeof (zone_pkey));
224   if (NULL != uri)
225   {
226     GNUNET_free (uri);
227     uri = NULL;
228   }
229   if (NULL != zm)
230   {
231     GNUNET_NAMESTORE_zone_monitor_stop (zm);
232     zm = NULL;
233   }
234   if (NULL != data)
235   {
236     GNUNET_free (data);
237     data = NULL;
238   }
239 }
240
241
242 /**
243  * Check if we are finished, and if so, perform shutdown.
244  */
245 static void
246 test_finished ()
247 {
248   if ( (NULL == add_qe) &&
249        (NULL == add_qe_uri) &&
250        (NULL == del_qe) &&
251        (NULL == reverse_qe) &&
252        (NULL == list_it) )
253     GNUNET_SCHEDULER_shutdown ();
254 }
255
256
257 /**
258  * Continuation called to notify client about result of the
259  * operation.
260  *
261  * @param cls closure, location of the QueueEntry pointer to NULL out
262  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
263  *                #GNUNET_NO if content was already there
264  *                #GNUNET_YES (or other positive value) on success
265  * @param emsg NULL on success, otherwise an error message
266  */
267 static void
268 add_continuation (void *cls,
269                   int32_t success,
270                   const char *emsg)
271 {
272   struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
273
274   *qe = NULL;
275   if (GNUNET_YES != success)
276   {
277     fprintf (stderr,
278              _("Adding record failed: %s\n"),
279              (GNUNET_NO == success) ? "record exists" : emsg);
280     if (GNUNET_NO != success)
281       ret = 1;
282   }
283   test_finished ();
284 }
285
286
287 /**
288  * Continuation called to notify client about result of the
289  * operation.
290  *
291  * @param cls closure, unused
292  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
293  *                #GNUNET_NO if content was already there
294  *                #GNUNET_YES (or other positive value) on success
295  * @param emsg NULL on success, otherwise an error message
296  */
297 static void
298 del_continuation (void *cls,
299                   int32_t success,
300                   const char *emsg)
301 {
302   del_qe = NULL;
303   if (success != GNUNET_YES)
304     fprintf (stderr,
305              _("Deleting record failed: %s\n"),
306              emsg);
307   test_finished ();
308 }
309
310
311 /**
312  * Process a record that was stored in the namestore.
313  *
314  * @param cls closure
315  * @param zone_key private key of the zone
316  * @param rname name that is being mapped (at most 255 characters long)
317  * @param rd_len number of entries in @a rd array
318  * @param rd array of records with data to store
319  */
320 static void
321 display_record (void *cls,
322                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
323                 const char *rname,
324                 unsigned int rd_len,
325                 const struct GNUNET_GNSRECORD_Data *rd)
326 {
327   const char *typestring;
328   char *s;
329   unsigned int i;
330   const char *ets;
331   struct GNUNET_TIME_Absolute at;
332   struct GNUNET_TIME_Relative rt;
333
334   if (NULL == rname)
335   {
336     list_it = NULL;
337     test_finished ();
338     return;
339   }
340   if ( (NULL != name) &&
341        (0 != strcmp (name, rname)) )
342   {
343     GNUNET_NAMESTORE_zone_iterator_next (list_it);
344     return;
345   }
346   FPRINTF (stdout,
347            "%s:\n",
348            rname);
349   for (i=0;i<rd_len;i++)
350   {
351     typestring = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
352     s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
353                                           rd[i].data,
354                                           rd[i].data_size);
355     if (NULL == s)
356     {
357       FPRINTF (stdout, _("\tCorrupt or unsupported record of type %u\n"),
358                (unsigned int) rd[i].record_type);
359       continue;
360     }
361     if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
362     {
363       rt.rel_value_us = rd[i].expiration_time;
364       ets = GNUNET_STRINGS_relative_time_to_string (rt, GNUNET_YES);
365     }
366     else
367     {
368       at.abs_value_us = rd[i].expiration_time;
369       ets = GNUNET_STRINGS_absolute_time_to_string (at);
370     }
371     FPRINTF (stdout,
372              "\t%s: %s (%s)\n",
373              typestring,
374              s,
375              ets);
376     GNUNET_free (s);
377   }
378   FPRINTF (stdout, "%s", "\n");
379   GNUNET_NAMESTORE_zone_iterator_next (list_it);
380 }
381
382
383 /**
384  * Function called once we are in sync in monitor mode.
385  *
386  * @param cls NULL
387  */
388 static void
389 sync_cb (void *cls)
390 {
391   FPRINTF (stdout, "%s", "Monitor is now in sync.\n");
392 }
393
394
395 /**
396  * We're storing a record; this function is given the existing record
397  * so that we can merge the information.
398  *
399  * @param cls closure, unused
400  * @param zone_key private key of the zone
401  * @param rec_name name that is being mapped (at most 255 characters long)
402  * @param rd_count number of entries in @a rd array
403  * @param rd array of records with data to store
404  */
405 static void
406 get_existing_record (void *cls,
407                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
408                      const char *rec_name,
409                      unsigned int rd_count,
410                      const struct GNUNET_GNSRECORD_Data *rd)
411 {
412   struct GNUNET_GNSRECORD_Data rdn[rd_count + 1];
413   struct GNUNET_GNSRECORD_Data *rde;
414
415   add_qe = NULL;
416   if ( (NULL != zone_key) &&
417        (0 != strcmp (rec_name, name)) )
418   {
419     GNUNET_break (0);
420     return;
421   }
422   memset (rdn, 0, sizeof (struct GNUNET_GNSRECORD_Data));
423   memcpy (&rdn[1], rd, rd_count * sizeof (struct GNUNET_GNSRECORD_Data));
424   /* FIXME: should add some logic to overwrite records if there
425      can only be one record of a particular type, and to check
426      if the combination of records is valid to begin with... */
427   rde = &rdn[0];
428   rde->data = data;
429   rde->data_size = data_size;
430   rde->record_type = type;
431   if (1 != shadow)
432     rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
433   if (1 != public)
434     rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
435   if (GNUNET_YES == etime_is_rel)
436   {
437     rde->expiration_time = etime_rel.rel_value_us;
438     rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
439   }
440   else if (GNUNET_NO == etime_is_rel)
441     rde->expiration_time = etime_abs.abs_value_us;
442   else
443     rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
444   GNUNET_assert (NULL != name);
445   add_qe = GNUNET_NAMESTORE_records_store (ns,
446                                            &zone_pkey,
447                                            name,
448                                            rd_count + 1,
449                                            rde,
450                                            &add_continuation,
451                                            &add_qe);
452 }
453
454
455 /**
456  * Function called with the result of our attempt to obtain a name for a given
457  * public key.
458  *
459  * @param cls NULL
460  * @param zone private key of the zone; NULL on disconnect
461  * @param label label of the records; NULL on disconnect
462  * @param rd_count number of entries in @a rd array, 0 if label was deleted
463  * @param rd array of records with data to store
464  */
465 static void
466 handle_reverse_lookup (void *cls,
467                        const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
468                        const char *label,
469                        unsigned int rd_count,
470                        const struct GNUNET_GNSRECORD_Data *rd)
471 {
472   reverse_qe = NULL;
473   if (NULL == label)
474     FPRINTF (stdout,
475              "%s.zkey\n",
476              reverse_pkey);
477   else
478     FPRINTF (stdout,
479              "%s.gnu\n",
480              label);
481   test_finished ();
482 }
483
484
485 /**
486  * Function called with the result from the check if the namestore
487  * service is actually running.  If it is, we start the actual
488  * operation.
489  *
490  * @param cls closure with our configuration
491  * @param result #GNUNET_YES if the namestore service is running
492  */
493 static void
494 testservice_task (void *cls,
495                   int result)
496 {
497   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
498   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
499   struct GNUNET_GNSRECORD_Data rd;
500
501   if (GNUNET_YES != result)
502   {
503     FPRINTF (stderr, _("Service `%s' is not running\n"),
504              "namestore");
505     return;
506   }
507   if (! (add|del|list|(NULL != uri)|(NULL != reverse_pkey)) )
508   {
509     /* nothing more to be done */
510     fprintf (stderr,
511              _("No options given\n"));
512     GNUNET_SCHEDULER_shutdown ();
513     return;
514   }
515   GNUNET_CRYPTO_ecdsa_key_get_public (&zone_pkey,
516                                     &pub);
517
518   ns = GNUNET_NAMESTORE_connect (cfg);
519   if (NULL == ns)
520   {
521     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
522                 _("Failed to connect to namestore\n"));
523     return;
524   }
525   if (add)
526   {
527     if (NULL == name)
528     {
529       fprintf (stderr,
530                _("Missing option `%s' for operation `%s'\n"),
531                "-n", _("add"));
532       GNUNET_SCHEDULER_shutdown ();
533       ret = 1;
534       return;
535     }
536     if (NULL == typestring)
537     {
538       fprintf (stderr,
539                _("Missing option `%s' for operation `%s'\n"),
540                "-t", _("add"));
541       GNUNET_SCHEDULER_shutdown ();
542       ret = 1;
543       return;
544     }
545     type = GNUNET_GNSRECORD_typename_to_number (typestring);
546     if (UINT32_MAX == type)
547     {
548       fprintf (stderr, _("Unsupported type `%s'\n"), typestring);
549       GNUNET_SCHEDULER_shutdown ();
550       ret = 1;
551       return;
552     }
553     if (NULL == value)
554     {
555       fprintf (stderr,
556                _("Missing option `%s' for operation `%s'\n"),
557                "-V", _("add"));
558       ret = 1;
559       GNUNET_SCHEDULER_shutdown ();
560       return;
561     }
562     if (GNUNET_OK !=
563         GNUNET_GNSRECORD_string_to_value (type,
564                                           value,
565                                           &data,
566                                           &data_size))
567     {
568       fprintf (stderr, _("Value `%s' invalid for record type `%s'\n"),
569                value,
570                typestring);
571       GNUNET_SCHEDULER_shutdown ();
572       ret = 1;
573       return;
574     }
575     if (NULL == expirationstring)
576     {
577       fprintf (stderr,
578                _("Missing option `%s' for operation `%s'\n"),
579                "-e", _("add"));
580       GNUNET_SCHEDULER_shutdown ();
581       ret = 1;
582       return;
583     }
584     if (0 == strcmp (expirationstring, "never"))
585     {
586       etime_abs = GNUNET_TIME_UNIT_FOREVER_ABS;
587       etime_is_rel = GNUNET_NO;
588     }
589     else if (GNUNET_OK ==
590              GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
591                                                     &etime_rel))
592     {
593       etime_is_rel = GNUNET_YES;
594     }
595     else if (GNUNET_OK ==
596              GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
597                                                     &etime_abs))
598     {
599       etime_is_rel = GNUNET_NO;
600     }
601     else
602     {
603       fprintf (stderr,
604                _("Invalid time format `%s'\n"),
605                expirationstring);
606       GNUNET_SCHEDULER_shutdown ();
607       ret = 1;
608       return;
609     }
610     add_qe = GNUNET_NAMESTORE_records_lookup (ns, &zone_pkey, name,
611         &get_existing_record, NULL );
612   }
613   if (del)
614   {
615     if (NULL == name)
616     {
617       fprintf (stderr,
618                _("Missing option `%s' for operation `%s'\n"),
619                "-n", _("del"));
620       GNUNET_SCHEDULER_shutdown ();
621       ret = 1;
622       return;
623     }
624     del_qe = GNUNET_NAMESTORE_records_store (ns,
625                                              &zone_pkey,
626                                              name,
627                                              0, NULL,
628                                              &del_continuation,
629                                              NULL);
630   }
631   if (list)
632   {
633     list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
634                                                      &zone_pkey,
635                                                      &display_record,
636                                                      NULL);
637   }
638   if (NULL != reverse_pkey)
639   {
640     struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
641
642     if (GNUNET_OK !=
643         GNUNET_CRYPTO_ecdsa_public_key_from_string (reverse_pkey,
644                                                        strlen (reverse_pkey),
645                                                        &pubkey))
646     {
647       fprintf (stderr,
648                _("Invalid public key for reverse lookup `%s'\n"),
649                reverse_pkey);
650       GNUNET_SCHEDULER_shutdown ();
651     }
652     reverse_qe = GNUNET_NAMESTORE_zone_to_name (ns,
653                                                 &zone_pkey,
654                                                 &pubkey,
655                                                 &handle_reverse_lookup,
656                                                 NULL);
657   }
658   if (NULL != uri)
659   {
660     char sh[105];
661     char sname[64];
662     struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
663
664     if ( (2 != (sscanf (uri,
665                         "gnunet://gns/%104s/%63s",
666                         sh,
667                         sname)) ) ||
668          (GNUNET_OK !=
669           GNUNET_CRYPTO_ecdsa_public_key_from_string (sh, strlen (sh), &pkey)) )
670     {
671       fprintf (stderr,
672                _("Invalid URI `%s'\n"),
673                uri);
674       GNUNET_SCHEDULER_shutdown ();
675       ret = 1;
676       return;
677     }
678     memset (&rd, 0, sizeof (rd));
679     rd.data = &pkey;
680     rd.data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
681     rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
682     if (GNUNET_YES == etime_is_rel)
683     {
684       rd.expiration_time = etime_rel.rel_value_us;
685       rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
686     }
687     else if (GNUNET_NO == etime_is_rel)
688       rd.expiration_time = etime_abs.abs_value_us;
689     else
690       rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
691     if (1 != shadow)
692       rd.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
693     add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
694                                                  &zone_pkey,
695                                                  sname,
696                                                  1,
697                                                  &rd,
698                                                  &add_continuation,
699                                                  &add_qe_uri);
700   }
701   if (monitor)
702   {
703     zm = GNUNET_NAMESTORE_zone_monitor_start (cfg,
704                                               &zone_pkey,
705                                               GNUNET_YES,
706                                               &display_record,
707                                               &sync_cb,
708                                               NULL);
709   }
710 }
711
712
713 /**
714  * Callback invoked from identity service with ego information.
715  * An @a ego of NULL means the ego was not found.
716  *
717  * @param cls closure with the configuration
718  * @param ego an ego known to identity service, or NULL
719  */
720 static void
721 identity_cb (void *cls,
722              const struct GNUNET_IDENTITY_Ego *ego)
723 {
724   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
725
726   el = NULL;
727   if (NULL == ego)
728   {
729     fprintf (stderr,
730              _("Ego `%s' not known to identity service\n"),
731              ego_name);
732     GNUNET_SCHEDULER_shutdown ();
733     return;
734   }
735   zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
736   GNUNET_free (ego_name);
737   ego_name = NULL;
738   GNUNET_CLIENT_service_test ("namestore", cfg,
739                               GNUNET_TIME_UNIT_SECONDS,
740                               &testservice_task,
741                               (void *) cfg);
742 }
743
744
745 /**
746  * Main function that will be run.
747  *
748  * @param cls closure
749  * @param args remaining command-line arguments
750  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
751  * @param cfg configuration
752  */
753 static void
754 run (void *cls, char *const *args, const char *cfgfile,
755      const struct GNUNET_CONFIGURATION_Handle *cfg)
756 {
757   if (NULL == ego_name)
758   {
759     fprintf (stderr,
760              _("You must specify which zone should be accessed\n"));
761     return;
762   }
763   if ( (NULL != args[0]) && (NULL == uri) )
764     uri = GNUNET_strdup (args[0]);
765   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
766                                 &do_shutdown, NULL);
767   el = GNUNET_IDENTITY_ego_lookup (cfg,
768                                    ego_name,
769                                    &identity_cb,
770                                    (void *) cfg);
771 }
772
773
774 /**
775  * The main function for gnunet-namestore.
776  *
777  * @param argc number of arguments from the command line
778  * @param argv command line arguments
779  * @return 0 ok, 1 on error
780  */
781 int
782 main (int argc, char *const *argv)
783 {
784   public = -1;
785
786   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
787     {'a', "add", NULL,
788      gettext_noop ("add record"), 0,
789      &GNUNET_GETOPT_set_one, &add},
790     {'d', "delete", NULL,
791      gettext_noop ("delete record"), 0,
792      &GNUNET_GETOPT_set_one, &del},
793     {'D', "display", NULL,
794      gettext_noop ("display records"), 0,
795      &GNUNET_GETOPT_set_one, &list},
796     {'e', "expiration", "TIME",
797      gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"), 1,
798      &GNUNET_GETOPT_set_string, &expirationstring},
799     {'m', "monitor", NULL,
800      gettext_noop ("monitor changes in the namestore"), 0,
801      &GNUNET_GETOPT_set_one, &monitor},
802     {'n', "name", "NAME",
803      gettext_noop ("name of the record to add/delete/display"), 1,
804      &GNUNET_GETOPT_set_string, &name},
805     {'r', "reverse", "PKEY",
806      gettext_noop ("determine our name for the given PKEY"), 1,
807      &GNUNET_GETOPT_set_string, &reverse_pkey},
808     {'t', "type", "TYPE",
809      gettext_noop ("type of the record to add/delete/display"), 1,
810      &GNUNET_GETOPT_set_string, &typestring},
811     {'u', "uri", "URI",
812      gettext_noop ("URI to import into our zone"), 1,
813      &GNUNET_GETOPT_set_string, &uri},
814     {'V', "value", "VALUE",
815      gettext_noop ("value of the record to add/delete"), 1,
816      &GNUNET_GETOPT_set_string, &value},
817     {'p', "public", NULL,
818      gettext_noop ("create or list public record"), 0,
819      &GNUNET_GETOPT_set_one, &public},
820     {'s', "shadow", NULL,
821      gettext_noop ("create shadow record (only valid if all other records of the same type have expired"), 0,
822      &GNUNET_GETOPT_set_one, &shadow},
823     {'z', "zone", "EGO",
824      gettext_noop ("name of the ego controlling the zone"), 1,
825      &GNUNET_GETOPT_set_string, &ego_name},
826     GNUNET_GETOPT_OPTION_END
827   };
828
829   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
830     return 2;
831
832   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
833   if (GNUNET_OK !=
834       GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
835                           _("GNUnet zone manipulation tool"),
836                           options,
837                           &run, NULL))
838   {
839     GNUNET_free ((void*) argv);
840     GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
841     return 1;
842   }
843   GNUNET_free ((void*) argv);
844   GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
845   return ret;
846 }
847
848 /* end of gnunet-namestore.c */