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