5f4daa4a844435aa90ae1111fc9d00c0716b50a6
[oweals/gnunet.git] / src / namestore / gnunet-namestore.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2013, 2014, 2019 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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  * - test
27  */
28 #include "platform.h"
29 #include <gnunet_util_lib.h>
30 #include <gnunet_dnsparser_lib.h>
31 #include <gnunet_identity_service.h>
32 #include <gnunet_gnsrecord_lib.h>
33 #include <gnunet_gns_service.h>
34 #include <gnunet_namestore_service.h>
35
36
37 /**
38  * Entry in record set for bulk processing.
39  */
40 struct RecordSetEntry
41 {
42   /**
43    * Kept in a linked list.
44    */
45   struct RecordSetEntry *next;
46
47   /**
48    * The record to add/remove.
49    */
50   struct GNUNET_GNSRECORD_Data record;
51 };
52
53
54 /**
55  * Handle to the namestore.
56  */
57 static struct GNUNET_NAMESTORE_Handle *ns;
58
59 /**
60  * Private key for the our zone.
61  */
62 static struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
63
64 /**
65  * Handle to identity lookup.
66  */
67 static struct GNUNET_IDENTITY_EgoLookup *el;
68
69 /**
70  * Identity service handle
71  */
72 static struct GNUNET_IDENTITY_Handle *idh;
73
74 /**
75  * Obtain default ego
76  */
77 struct GNUNET_IDENTITY_Operation *get_default;
78
79 /**
80  * Name of the ego controlling the zone.
81  */
82 static char *ego_name;
83
84 /**
85  * Desired action is to add a record.
86  */
87 static int add;
88
89 /**
90  * Queue entry for the 'add-uri' operation.
91  */
92 static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri;
93
94 /**
95  * Queue entry for the 'add' operation.
96  */
97 static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
98
99 /**
100  * Queue entry for the 'lookup' operation.
101  */
102 static struct GNUNET_NAMESTORE_QueueEntry *get_qe;
103
104 /**
105  * Queue entry for the 'reverse lookup' operation (in combination with a name).
106  */
107 static struct GNUNET_NAMESTORE_QueueEntry *reverse_qe;
108
109 /**
110  * Desired action is to list records.
111  */
112 static int list;
113
114 /**
115  * List iterator for the 'list' operation.
116  */
117 static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
118
119 /**
120  * Desired action is to remove a record.
121  */
122 static int del;
123
124 /**
125  * Is record public (opposite of #GNUNET_GNSRECORD_RF_PRIVATE)
126  */
127 static int is_public;
128
129 /**
130  * Is record a shadow record (#GNUNET_GNSRECORD_RF_SHADOW_RECORD)
131  */
132 static int is_shadow;
133
134 /**
135  * Queue entry for the 'del' operation.
136  */
137 static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
138
139 /**
140  * Queue entry for the 'set/replace' operation.
141  */
142 static struct GNUNET_NAMESTORE_QueueEntry *set_qe;
143
144 /**
145  * Name of the records to add/list/remove.
146  */
147 static char *name;
148
149 /**
150  * Value of the record to add/remove.
151  */
152 static char *value;
153
154 /**
155  * URI to import.
156  */
157 static char *uri;
158
159 /**
160  * Reverse lookup to perform.
161  */
162 static char *reverse_pkey;
163
164 /**
165  * Type of the record to add/remove, NULL to remove all.
166  */
167 static char *typestring;
168
169 /**
170  * Desired expiration time.
171  */
172 static char *expirationstring;
173
174 /**
175  * Desired nick name.
176  */
177 static char *nickstring;
178
179 /**
180  * Global return value
181  */
182 static int ret;
183
184 /**
185  * Type string converted to DNS type value.
186  */
187 static uint32_t type;
188
189 /**
190  * Value in binary format.
191  */
192 static void *data;
193
194 /**
195  * Number of bytes in #data.
196  */
197 static size_t data_size;
198
199 /**
200  * Expiration string converted to numeric value.
201  */
202 static uint64_t etime;
203
204 /**
205  * Is expiration time relative or absolute time?
206  */
207 static int etime_is_rel = GNUNET_SYSERR;
208
209 /**
210  * Monitor handle.
211  */
212 static struct GNUNET_NAMESTORE_ZoneMonitor *zm;
213
214 /**
215  * Enables monitor mode.
216  */
217 static int monitor;
218
219 /**
220  * Entry in record set for processing records in bulk.
221  */
222 static struct RecordSetEntry *recordset;
223
224
225 /**
226  * Task run on shutdown.  Cleans up everything.
227  *
228  * @param cls unused
229  */
230 static void
231 do_shutdown (void *cls)
232 {
233   (void) cls;
234   if (NULL != get_default)
235   {
236     GNUNET_IDENTITY_cancel (get_default);
237     get_default = NULL;
238   }
239   if (NULL != idh)
240   {
241     GNUNET_IDENTITY_disconnect (idh);
242     idh = NULL;
243   }
244   if (NULL != el)
245   {
246     GNUNET_IDENTITY_ego_lookup_cancel (el);
247     el = NULL;
248   }
249   if (NULL != list_it)
250   {
251     GNUNET_NAMESTORE_zone_iteration_stop (list_it);
252     list_it = NULL;
253   }
254   if (NULL != add_qe)
255   {
256     GNUNET_NAMESTORE_cancel (add_qe);
257     add_qe = NULL;
258   }
259   if (NULL != set_qe)
260   {
261     GNUNET_NAMESTORE_cancel (set_qe);
262     set_qe = NULL;
263   }
264   if (NULL != add_qe_uri)
265   {
266     GNUNET_NAMESTORE_cancel (add_qe_uri);
267     add_qe_uri = NULL;
268   }
269   if (NULL != get_qe)
270   {
271     GNUNET_NAMESTORE_cancel (get_qe);
272     get_qe = NULL;
273   }
274   if (NULL != del_qe)
275   {
276     GNUNET_NAMESTORE_cancel (del_qe);
277     del_qe = NULL;
278   }
279   if (NULL != ns)
280   {
281     GNUNET_NAMESTORE_disconnect (ns);
282     ns = NULL;
283   }
284   memset (&zone_pkey, 0, sizeof (zone_pkey));
285   if (NULL != uri)
286   {
287     GNUNET_free (uri);
288     uri = NULL;
289   }
290   if (NULL != zm)
291   {
292     GNUNET_NAMESTORE_zone_monitor_stop (zm);
293     zm = NULL;
294   }
295   if (NULL != data)
296   {
297     GNUNET_free (data);
298     data = NULL;
299   }
300 }
301
302
303 /**
304  * Check if we are finished, and if so, perform shutdown.
305  */
306 static void
307 test_finished ()
308 {
309   if ((NULL == add_qe) && (NULL == add_qe_uri) && (NULL == get_qe) &&
310       (NULL == del_qe) && (NULL == reverse_qe) && (NULL == list_it))
311     GNUNET_SCHEDULER_shutdown ();
312 }
313
314
315 /**
316  * Continuation called to notify client about result of the
317  * operation.
318  *
319  * @param cls closure, location of the QueueEntry pointer to NULL out
320  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
321  *                #GNUNET_NO if content was already there
322  *                #GNUNET_YES (or other positive value) on success
323  * @param emsg NULL on success, otherwise an error message
324  */
325 static void
326 add_continuation (void *cls, int32_t success, const char *emsg)
327 {
328   struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
329
330   *qe = NULL;
331   if (GNUNET_YES != success)
332   {
333     fprintf (stderr,
334              _ ("Adding record failed: %s\n"),
335              (GNUNET_NO == success) ? "record exists" : emsg);
336     if (GNUNET_NO != success)
337       ret = 1;
338   }
339   ret = 0;
340   test_finished ();
341 }
342
343
344 /**
345  * Continuation called to notify client about result of the
346  * operation.
347  *
348  * @param cls closure, unused
349  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
350  *                #GNUNET_NO if content was already there
351  *                #GNUNET_YES (or other positive value) on success
352  * @param emsg NULL on success, otherwise an error message
353  */
354 static void
355 del_continuation (void *cls, int32_t success, const char *emsg)
356 {
357   (void) cls;
358   del_qe = NULL;
359   if (GNUNET_NO == success)
360   {
361     fprintf (stderr,
362              _ ("Deleting record failed, record does not exist%s%s\n"),
363              (NULL != emsg) ? ": " : "",
364              (NULL != emsg) ? emsg : "");
365   }
366   if (GNUNET_SYSERR == success)
367   {
368     fprintf (stderr,
369              _ ("Deleting record failed%s%s\n"),
370              (NULL != emsg) ? ": " : "",
371              (NULL != emsg) ? emsg : "");
372   }
373   test_finished ();
374 }
375
376
377 /**
378  * Function called when we are done with a zone iteration.
379  */
380 static void
381 zone_iteration_finished (void *cls)
382 {
383   (void) cls;
384   list_it = NULL;
385   test_finished ();
386 }
387
388
389 /**
390  * Function called when we encountered an error in a zone iteration.
391  */
392 static void
393 zone_iteration_error_cb (void *cls)
394 {
395   (void) cls;
396   list_it = NULL;
397   fprintf (stderr, "Error iterating over zone\n");
398   ret = 1;
399   test_finished ();
400 }
401
402
403 /**
404  * Process a record that was stored in the namestore.
405  *
406  * @param rname name that is being mapped (at most 255 characters long)
407  * @param rd_len number of entries in @a rd array
408  * @param rd array of records with data to store
409  */
410 static void
411 display_record (const char *rname,
412                 unsigned int rd_len,
413                 const struct GNUNET_GNSRECORD_Data *rd)
414 {
415   const char *typestr;
416   char *s;
417   const char *ets;
418   struct GNUNET_TIME_Absolute at;
419   struct GNUNET_TIME_Relative rt;
420   int have_record;
421
422   if ((NULL != name) && (0 != strcmp (name, rname)))
423   {
424     GNUNET_NAMESTORE_zone_iterator_next (list_it, 1);
425     return;
426   }
427   have_record = GNUNET_NO;
428   for (unsigned int i = 0; i < rd_len; i++)
429   {
430     if ((GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
431         (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT)))
432       continue;
433     if ((type != rd[i].record_type) && (GNUNET_GNSRECORD_TYPE_ANY != type))
434       continue;
435     have_record = GNUNET_YES;
436     break;
437   }
438   if (GNUNET_NO == have_record)
439     return;
440   FPRINTF (stdout, "%s:\n", rname);
441   if (NULL != typestring)
442     type = GNUNET_GNSRECORD_typename_to_number (typestring);
443   else
444     type = GNUNET_GNSRECORD_TYPE_ANY;
445   for (unsigned int i = 0; i < rd_len; i++)
446   {
447     if ((GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
448         (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT)))
449       continue;
450     if ((type != rd[i].record_type) && (GNUNET_GNSRECORD_TYPE_ANY != type))
451       continue;
452     typestr = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
453     s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
454                                           rd[i].data,
455                                           rd[i].data_size);
456     if (NULL == s)
457     {
458       FPRINTF (stdout,
459                _ ("\tCorrupt or unsupported record of type %u\n"),
460                (unsigned int) rd[i].record_type);
461       continue;
462     }
463     if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
464     {
465       rt.rel_value_us = rd[i].expiration_time;
466       ets = GNUNET_STRINGS_relative_time_to_string (rt, GNUNET_YES);
467     }
468     else
469     {
470       at.abs_value_us = rd[i].expiration_time;
471       ets = GNUNET_STRINGS_absolute_time_to_string (at);
472     }
473     FPRINTF (stdout,
474              "\t%s: %s (%s)\t%s\t%s\n",
475              typestr,
476              s,
477              ets,
478              (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE)) ? "PRIVATE"
479                                                                 : "PUBLIC",
480              (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD)) ? "SHADOW"
481                                                                       : "");
482     GNUNET_free (s);
483   }
484   FPRINTF (stdout, "%s", "\n");
485 }
486
487
488 /**
489  * Process a record that was stored in the namestore.
490  *
491  * @param cls closure
492  * @param zone_key private key of the zone
493  * @param rname name that is being mapped (at most 255 characters long)
494  * @param rd_len number of entries in @a rd array
495  * @param rd array of records with data to store
496  */
497 static void
498 display_record_iterator (void *cls,
499                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
500                          const char *rname,
501                          unsigned int rd_len,
502                          const struct GNUNET_GNSRECORD_Data *rd)
503 {
504   (void) cls;
505   (void) zone_key;
506   display_record (rname, rd_len, rd);
507   GNUNET_NAMESTORE_zone_iterator_next (list_it, 1);
508 }
509
510
511 /**
512  * Process a record that was stored in the namestore.
513  *
514  * @param cls closure
515  * @param zone_key private key of the zone
516  * @param rname name that is being mapped (at most 255 characters long)
517  * @param rd_len number of entries in @a rd array
518  * @param rd array of records with data to store
519  */
520 static void
521 display_record_monitor (void *cls,
522                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
523                         const char *rname,
524                         unsigned int rd_len,
525                         const struct GNUNET_GNSRECORD_Data *rd)
526 {
527   (void) cls;
528   (void) zone_key;
529   display_record (rname, rd_len, rd);
530   GNUNET_NAMESTORE_zone_monitor_next (zm, 1);
531 }
532
533
534 /**
535  * Process a record that was stored in the namestore.
536  *
537  * @param cls closure
538  * @param zone_key private key of the zone
539  * @param rname name that is being mapped (at most 255 characters long)
540  * @param rd_len number of entries in @a rd array
541  * @param rd array of records with data to store
542  */
543 static void
544 display_record_lookup (void *cls,
545                        const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
546                        const char *rname,
547                        unsigned int rd_len,
548                        const struct GNUNET_GNSRECORD_Data *rd)
549 {
550   (void) cls;
551   (void) zone_key;
552   get_qe = NULL;
553   display_record (rname, rd_len, rd);
554   test_finished ();
555 }
556
557
558 /**
559  * Function called once we are in sync in monitor mode.
560  *
561  * @param cls NULL
562  */
563 static void
564 sync_cb (void *cls)
565 {
566   (void) cls;
567   FPRINTF (stdout, "%s", "Monitor is now in sync.\n");
568 }
569
570
571 /**
572  * Function called on errors while monitoring.
573  *
574  * @param cls NULL
575  */
576 static void
577 monitor_error_cb (void *cls)
578 {
579   (void) cls;
580   FPRINTF (stderr, "%s", "Monitor disconnected and out of sync.\n");
581 }
582
583
584 /**
585  * Function called on errors while monitoring.
586  *
587  * @param cls NULL
588  */
589 static void
590 lookup_error_cb (void *cls)
591 {
592   (void) cls;
593   get_qe = NULL;
594   FPRINTF (stderr, "%s", "Failed to lookup record.\n");
595   test_finished ();
596 }
597
598
599 /**
600  * Function called if lookup fails.
601  */
602 static void
603 add_error_cb (void *cls)
604 {
605   (void) cls;
606   add_qe = NULL;
607   GNUNET_break (0);
608   ret = 1;
609   test_finished ();
610 }
611
612
613 /**
614  * We're storing a record; this function is given the existing record
615  * so that we can merge the information.
616  *
617  * @param cls closure, unused
618  * @param zone_key private key of the zone
619  * @param rec_name name that is being mapped (at most 255 characters long)
620  * @param rd_count number of entries in @a rd array
621  * @param rd array of records with data to store
622  */
623 static void
624 get_existing_record (void *cls,
625                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
626                      const char *rec_name,
627                      unsigned int rd_count,
628                      const struct GNUNET_GNSRECORD_Data *rd)
629 {
630   struct GNUNET_GNSRECORD_Data rdn[rd_count + 1];
631   struct GNUNET_GNSRECORD_Data *rde;
632
633   (void) cls;
634   (void) zone_key;
635   add_qe = NULL;
636   if (0 != strcmp (rec_name, name))
637   {
638     GNUNET_break (0);
639     ret = 1;
640     test_finished ();
641     return;
642   }
643
644   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
645               "Received %u records for name `%s'\n",
646               rd_count,
647               rec_name);
648   for (unsigned int i = 0; i < rd_count; i++)
649   {
650     switch (rd[i].record_type)
651     {
652     case GNUNET_DNSPARSER_TYPE_CNAME:
653       fprintf (
654         stderr,
655         _ (
656           "A %s record exists already under `%s', no other records can be added.\n"),
657         "CNAME",
658         rec_name);
659       ret = 1;
660       test_finished ();
661       return;
662     case GNUNET_GNSRECORD_TYPE_PKEY:
663       fprintf (
664         stderr,
665         _ (
666           "A %s record exists already under `%s', no other records can be added.\n"),
667         "PKEY",
668         rec_name);
669       ret = 1;
670       test_finished ();
671       return;
672     case GNUNET_DNSPARSER_TYPE_SOA:
673       if (GNUNET_DNSPARSER_TYPE_SOA == type)
674       {
675         fprintf (
676           stderr,
677           _ (
678             "A SOA record exists already under `%s', cannot add a second SOA to the same zone.\n"),
679           rec_name);
680         ret = 1;
681         test_finished ();
682         return;
683       }
684       break;
685     }
686   }
687   switch (type)
688   {
689   case GNUNET_DNSPARSER_TYPE_CNAME:
690     if (0 != rd_count)
691     {
692       fprintf (stderr,
693                _ (
694                  "Records already exist under `%s', cannot add `%s' record.\n"),
695                rec_name,
696                "CNAME");
697       ret = 1;
698       test_finished ();
699       return;
700     }
701     break;
702   case GNUNET_GNSRECORD_TYPE_PKEY:
703     if (0 != rd_count)
704     {
705       fprintf (stderr,
706                _ (
707                  "Records already exist under `%s', cannot add `%s' record.\n"),
708                rec_name,
709                "PKEY");
710       ret = 1;
711       test_finished ();
712       return;
713     }
714     break;
715   case GNUNET_GNSRECORD_TYPE_GNS2DNS:
716     for (unsigned int i = 0; i < rd_count; i++)
717       if (GNUNET_GNSRECORD_TYPE_GNS2DNS != rd[i].record_type)
718       {
719         fprintf (
720           stderr,
721           _ (
722             "Non-GNS2DNS records already exist under `%s', cannot add GNS2DNS record.\n"),
723           rec_name);
724         ret = 1;
725         test_finished ();
726         return;
727       }
728     break;
729   }
730   memset (rdn, 0, sizeof (struct GNUNET_GNSRECORD_Data));
731   GNUNET_memcpy (&rdn[1], rd, rd_count * sizeof (struct GNUNET_GNSRECORD_Data));
732   rde = &rdn[0];
733   rde->data = data;
734   rde->data_size = data_size;
735   rde->record_type = type;
736   if (1 == is_shadow)
737     rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
738   if (1 != is_public)
739     rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
740   rde->expiration_time = etime;
741   if (GNUNET_YES == etime_is_rel)
742     rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
743   else if (GNUNET_NO != etime_is_rel)
744     rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
745   GNUNET_assert (NULL != name);
746   add_qe = GNUNET_NAMESTORE_records_store (ns,
747                                            &zone_pkey,
748                                            name,
749                                            rd_count + 1,
750                                            rde,
751                                            &add_continuation,
752                                            &add_qe);
753 }
754
755
756 /**
757  * Function called if we encountered an error in zone-to-name.
758  */
759 static void
760 reverse_error_cb (void *cls)
761 {
762   (void) cls;
763   reverse_qe = NULL;
764   FPRINTF (stdout, "%s.zkey\n", reverse_pkey);
765 }
766
767
768 /**
769  * Function called with the result of our attempt to obtain a name for a given
770  * public key.
771  *
772  * @param cls NULL
773  * @param zone private key of the zone; NULL on disconnect
774  * @param label label of the records; NULL on disconnect
775  * @param rd_count number of entries in @a rd array, 0 if label was deleted
776  * @param rd array of records with data to store
777  */
778 static void
779 handle_reverse_lookup (void *cls,
780                        const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
781                        const char *label,
782                        unsigned int rd_count,
783                        const struct GNUNET_GNSRECORD_Data *rd)
784 {
785   (void) cls;
786   (void) zone;
787   (void) rd_count;
788   (void) rd;
789   reverse_qe = NULL;
790   if (NULL == label)
791     FPRINTF (stdout, "%s\n", reverse_pkey);
792   else
793     FPRINTF (stdout, "%s.%s\n", label, ego_name);
794   test_finished ();
795 }
796
797
798 /**
799  * Function called if lookup for deletion fails.
800  */
801 static void
802 del_lookup_error_cb (void *cls)
803 {
804   (void) cls;
805   del_qe = NULL;
806   GNUNET_break (0);
807   ret = 1;
808   test_finished ();
809 }
810
811
812 /**
813  * We were asked to delete something; this function is called with
814  * the existing records. Now we should determine what should be
815  * deleted and then issue the deletion operation.
816  *
817  * @param cls NULL
818  * @param zone private key of the zone we are deleting from
819  * @param label name of the records we are editing
820  * @param rd_count size of the @a rd array
821  * @param rd existing records
822  */
823 static void
824 del_monitor (void *cls,
825              const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
826              const char *label,
827              unsigned int rd_count,
828              const struct GNUNET_GNSRECORD_Data *rd)
829 {
830   struct GNUNET_GNSRECORD_Data rdx[rd_count];
831   unsigned int rd_left;
832   uint32_t type;
833   char *vs;
834
835   (void) cls;
836   (void) zone;
837   del_qe = NULL;
838   if (0 == rd_count)
839   {
840     FPRINTF (stderr,
841              _ (
842                "There are no records under label `%s' that could be deleted.\n"),
843              label);
844     ret = 1;
845     test_finished ();
846     return;
847   }
848   if ((NULL == value) && (NULL == typestring))
849   {
850     /* delete everything */
851     del_qe = GNUNET_NAMESTORE_records_store (ns,
852                                              &zone_pkey,
853                                              name,
854                                              0,
855                                              NULL,
856                                              &del_continuation,
857                                              NULL);
858     return;
859   }
860   rd_left = 0;
861   if (NULL != typestring)
862     type = GNUNET_GNSRECORD_typename_to_number (typestring);
863   else
864     type = GNUNET_GNSRECORD_TYPE_ANY;
865   for (unsigned int i = 0; i < rd_count; i++)
866   {
867     vs = NULL;
868     if (! (((GNUNET_GNSRECORD_TYPE_ANY == type) ||
869             (rd[i].record_type == type)) &&
870            ((NULL == value) ||
871             (NULL ==
872              (vs = (GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
873                                                       rd[i].data,
874                                                       rd[i].data_size)))) ||
875             (0 == strcmp (vs, value)))))
876       rdx[rd_left++] = rd[i];
877     GNUNET_free_non_null (vs);
878   }
879   if (rd_count == rd_left)
880   {
881     /* nothing got deleted */
882     FPRINTF (
883       stderr,
884       _ (
885         "There are no records under label `%s' that match the request for deletion.\n"),
886       label);
887     test_finished ();
888     return;
889   }
890   /* delete everything but what we copied to 'rdx' */
891   del_qe = GNUNET_NAMESTORE_records_store (ns,
892                                            &zone_pkey,
893                                            name,
894                                            rd_left,
895                                            rdx,
896                                            &del_continuation,
897                                            NULL);
898 }
899
900
901 /**
902  * Parse expiration time.
903  *
904  * @param expirationstring text to parse
905  * @param etime_is_rel[out] set to #GNUNET_YES if time is relative
906  * @param etime[out] set to expiration time (abs or rel)
907  * @return #GNUNET_OK on success
908  */
909 static int
910 parse_expiration (const char *expirationstring,
911                   int *etime_is_rel,
912                   uint64_t *etime)
913 {
914   struct GNUNET_TIME_Relative etime_rel;
915   struct GNUNET_TIME_Absolute etime_abs;
916
917   if (0 == strcmp (expirationstring, "never"))
918   {
919     *etime = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
920     *etime_is_rel = GNUNET_NO;
921     return GNUNET_OK;
922   }
923   if (GNUNET_OK ==
924       GNUNET_STRINGS_fancy_time_to_relative (expirationstring, &etime_rel))
925   {
926     *etime_is_rel = GNUNET_YES;
927     *etime = etime_rel.rel_value_us;
928     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
929                 "Storing record with relative expiration time of %s\n",
930                 GNUNET_STRINGS_relative_time_to_string (etime_rel, GNUNET_NO));
931     return GNUNET_OK;
932   }
933   if (GNUNET_OK ==
934       GNUNET_STRINGS_fancy_time_to_absolute (expirationstring, &etime_abs))
935   {
936     *etime_is_rel = GNUNET_NO;
937     *etime = etime_abs.abs_value_us;
938     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939                 "Storing record with absolute expiration time of %s\n",
940                 GNUNET_STRINGS_absolute_time_to_string (etime_abs));
941     return GNUNET_OK;
942   }
943   return GNUNET_SYSERR;
944 }
945
946
947 /**
948  * Function called when namestore is done with the replace
949  * operation.
950  *
951  * @param cls NULL
952  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
953  *                #GNUNET_NO if content was already there or not found
954  *                #GNUNET_YES (or other positive value) on success
955  * @param emsg NULL on success, otherwise an error message
956  */
957 static void
958 replace_cont (void *cls, int success, const char *emsg)
959 {
960   (void) cls;
961
962   set_qe = NULL;
963   if (GNUNET_OK != success)
964   {
965     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
966                 _ ("Failed to replace records: %s\n"),
967                 emsg);
968     ret = 1; /* fail from 'main' */
969   }
970   GNUNET_SCHEDULER_shutdown ();
971 }
972
973
974 /**
975  * Callback invoked from identity service with ego information.
976  * An @a ego of NULL means the ego was not found.
977  *
978  * @param cls closure with the configuration
979  * @param ego an ego known to identity service, or NULL
980  */
981 static void
982 identity_cb (void *cls, const struct GNUNET_IDENTITY_Ego *ego)
983 {
984   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
985   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
986   struct GNUNET_GNSRECORD_Data rd;
987
988   el = NULL;
989   if ((NULL != name) && (0 != strchr (name, '.')))
990   {
991     fprintf (stderr,
992              _ ("Label `%s' contains `.' which is not allowed\n"),
993              name);
994     GNUNET_SCHEDULER_shutdown ();
995     ret = -1;
996     return;
997   }
998
999   if (NULL == ego)
1000   {
1001     if (NULL != ego_name)
1002     {
1003       fprintf (stderr,
1004                _ ("Ego `%s' not known to identity service\n"),
1005                ego_name);
1006     }
1007     GNUNET_SCHEDULER_shutdown ();
1008     ret = -1;
1009     return;
1010   }
1011   zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
1012   GNUNET_free_non_null (ego_name);
1013   ego_name = NULL;
1014
1015   if (! (add | del | list | (NULL != nickstring) | (NULL != uri) |
1016          (NULL != reverse_pkey) | (NULL != recordset)))
1017   {
1018     /* nothing more to be done */
1019     fprintf (stderr, _ ("No options given\n"));
1020     GNUNET_SCHEDULER_shutdown ();
1021     return;
1022   }
1023   GNUNET_CRYPTO_ecdsa_key_get_public (&zone_pkey, &pub);
1024   ns = GNUNET_NAMESTORE_connect (cfg);
1025   if (NULL == ns)
1026   {
1027     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1028                 _ ("Failed to connect to namestore\n"));
1029     return;
1030   }
1031
1032   if (NULL != recordset)
1033   {
1034     /* replace entire record set */
1035     unsigned int rd_count;
1036     struct GNUNET_GNSRECORD_Data *rd;
1037
1038     if (NULL == name)
1039     {
1040       fprintf (stderr,
1041                _ ("Missing option `%s' for operation `%s'\n"),
1042                "-R",
1043                _ ("replace"));
1044       GNUNET_SCHEDULER_shutdown ();
1045       ret = 1;
1046       return;
1047     }
1048     rd_count = 0;
1049     for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
1050       rd_count++;
1051     rd = GNUNET_new_array (rd_count, struct GNUNET_GNSRECORD_Data);
1052     rd_count = 0;
1053     for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
1054     {
1055       rd[rd_count] = e->record;
1056       rd_count++;
1057     }
1058     set_qe = GNUNET_NAMESTORE_records_store (ns,
1059                                              &zone_pkey,
1060                                              name,
1061                                              rd_count,
1062                                              rd,
1063                                              &replace_cont,
1064                                              NULL);
1065     GNUNET_free (rd);
1066     return;
1067   }
1068
1069   if (add)
1070   {
1071     if (NULL == name)
1072     {
1073       fprintf (stderr,
1074                _ ("Missing option `%s' for operation `%s'\n"),
1075                "-n",
1076                _ ("add"));
1077       GNUNET_SCHEDULER_shutdown ();
1078       ret = 1;
1079       return;
1080     }
1081     if (NULL == typestring)
1082     {
1083       fprintf (stderr,
1084                _ ("Missing option `%s' for operation `%s'\n"),
1085                "-t",
1086                _ ("add"));
1087       GNUNET_SCHEDULER_shutdown ();
1088       ret = 1;
1089       return;
1090     }
1091     type = GNUNET_GNSRECORD_typename_to_number (typestring);
1092     if (UINT32_MAX == type)
1093     {
1094       fprintf (stderr, _ ("Unsupported type `%s'\n"), typestring);
1095       GNUNET_SCHEDULER_shutdown ();
1096       ret = 1;
1097       return;
1098     }
1099     if (NULL == value)
1100     {
1101       fprintf (stderr,
1102                _ ("Missing option `%s' for operation `%s'\n"),
1103                "-V",
1104                _ ("add"));
1105       ret = 1;
1106       GNUNET_SCHEDULER_shutdown ();
1107       return;
1108     }
1109     if (GNUNET_OK !=
1110         GNUNET_GNSRECORD_string_to_value (type, value, &data, &data_size))
1111     {
1112       fprintf (stderr,
1113                _ ("Value `%s' invalid for record type `%s'\n"),
1114                value,
1115                typestring);
1116       GNUNET_SCHEDULER_shutdown ();
1117       ret = 1;
1118       return;
1119     }
1120     if (NULL == expirationstring)
1121     {
1122       fprintf (stderr,
1123                _ ("Missing option `%s' for operation `%s'\n"),
1124                "-e",
1125                _ ("add"));
1126       GNUNET_SCHEDULER_shutdown ();
1127       ret = 1;
1128       return;
1129     }
1130     if (GNUNET_OK != parse_expiration (expirationstring, &etime_is_rel, &etime))
1131     {
1132       fprintf (stderr, _ ("Invalid time format `%s'\n"), expirationstring);
1133       GNUNET_SCHEDULER_shutdown ();
1134       ret = 1;
1135       return;
1136     }
1137     add_qe = GNUNET_NAMESTORE_records_lookup (ns,
1138                                               &zone_pkey,
1139                                               name,
1140                                               &add_error_cb,
1141                                               NULL,
1142                                               &get_existing_record,
1143                                               NULL);
1144   }
1145   if (del)
1146   {
1147     if (NULL == name)
1148     {
1149       fprintf (stderr,
1150                _ ("Missing option `%s' for operation `%s'\n"),
1151                "-n",
1152                _ ("del"));
1153       GNUNET_SCHEDULER_shutdown ();
1154       ret = 1;
1155       return;
1156     }
1157     del_qe = GNUNET_NAMESTORE_records_lookup (ns,
1158                                               &zone_pkey,
1159                                               name,
1160                                               &del_lookup_error_cb,
1161                                               NULL,
1162                                               &del_monitor,
1163                                               NULL);
1164   }
1165   if (list)
1166   {
1167     if (NULL != name)
1168       get_qe = GNUNET_NAMESTORE_records_lookup (ns,
1169                                                 &zone_pkey,
1170                                                 name,
1171                                                 &lookup_error_cb,
1172                                                 NULL,
1173                                                 &display_record_lookup,
1174                                                 NULL);
1175     else
1176       list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
1177                                                        &zone_pkey,
1178                                                        &zone_iteration_error_cb,
1179                                                        NULL,
1180                                                        &display_record_iterator,
1181                                                        NULL,
1182                                                        &zone_iteration_finished,
1183                                                        NULL);
1184   }
1185   if (NULL != reverse_pkey)
1186   {
1187     struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
1188
1189     if (GNUNET_OK !=
1190         GNUNET_CRYPTO_ecdsa_public_key_from_string (reverse_pkey,
1191                                                     strlen (reverse_pkey),
1192                                                     &pubkey))
1193     {
1194       fprintf (stderr,
1195                _ ("Invalid public key for reverse lookup `%s'\n"),
1196                reverse_pkey);
1197       GNUNET_SCHEDULER_shutdown ();
1198     }
1199     reverse_qe = GNUNET_NAMESTORE_zone_to_name (ns,
1200                                                 &zone_pkey,
1201                                                 &pubkey,
1202                                                 &reverse_error_cb,
1203                                                 NULL,
1204                                                 &handle_reverse_lookup,
1205                                                 NULL);
1206   }
1207   if (NULL != uri)
1208   {
1209     char sh[105];
1210     char sname[64];
1211     struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1212
1213     GNUNET_STRINGS_utf8_tolower (uri, uri);
1214     if ((2 != (sscanf (uri, "gnunet://gns/%52s/%63s", sh, sname))) ||
1215         (GNUNET_OK !=
1216          GNUNET_CRYPTO_ecdsa_public_key_from_string (sh, strlen (sh), &pkey)))
1217     {
1218       fprintf (stderr, _ ("Invalid URI `%s'\n"), uri);
1219       GNUNET_SCHEDULER_shutdown ();
1220       ret = 1;
1221       return;
1222     }
1223     memset (&rd, 0, sizeof (rd));
1224     rd.data = &pkey;
1225     rd.data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
1226     rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
1227     rd.expiration_time = etime;
1228     if (GNUNET_YES == etime_is_rel)
1229       rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1230     if (1 == is_shadow)
1231       rd.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
1232     add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
1233                                                  &zone_pkey,
1234                                                  sname,
1235                                                  1,
1236                                                  &rd,
1237                                                  &add_continuation,
1238                                                  &add_qe_uri);
1239   }
1240   if (NULL != nickstring)
1241   {
1242     if (0 == strlen (nickstring))
1243     {
1244       fprintf (stderr, _ ("Invalid nick `%s'\n"), nickstring);
1245       GNUNET_SCHEDULER_shutdown ();
1246       ret = 1;
1247       return;
1248     }
1249     add_qe_uri = GNUNET_NAMESTORE_set_nick (ns,
1250                                             &zone_pkey,
1251                                             nickstring,
1252                                             &add_continuation,
1253                                             &add_qe_uri);
1254   }
1255   if (monitor)
1256   {
1257     zm = GNUNET_NAMESTORE_zone_monitor_start (cfg,
1258                                               &zone_pkey,
1259                                               GNUNET_YES,
1260                                               &monitor_error_cb,
1261                                               NULL,
1262                                               &display_record_monitor,
1263                                               NULL,
1264                                               &sync_cb,
1265                                               NULL);
1266   }
1267 }
1268
1269
1270 static void
1271 default_ego_cb (void *cls,
1272                 struct GNUNET_IDENTITY_Ego *ego,
1273                 void **ctx,
1274                 const char *name)
1275 {
1276   (void) cls;
1277   (void) ctx;
1278   (void) name;
1279   get_default = NULL;
1280   if (NULL == ego)
1281   {
1282     fprintf (stderr, _ ("No default ego configured in identity service\n"));
1283     GNUNET_SCHEDULER_shutdown ();
1284     ret = -1;
1285     return;
1286   }
1287   else
1288   {
1289     identity_cb (cls, ego);
1290   }
1291 }
1292
1293
1294 static void
1295 id_connect_cb (void *cls,
1296                struct GNUNET_IDENTITY_Ego *ego,
1297                void **ctx,
1298                const char *name)
1299 {
1300   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1301
1302   (void) cls;
1303   (void) ctx;
1304   (void) name;
1305   if (NULL == ego)
1306   {
1307     get_default =
1308       GNUNET_IDENTITY_get (idh, "namestore", &default_ego_cb, (void *) cfg);
1309   }
1310 }
1311
1312
1313 /**
1314  * Main function that will be run.
1315  *
1316  * @param cls closure
1317  * @param args remaining command-line arguments
1318  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1319  * @param cfg configuration
1320  */
1321 static void
1322 run (void *cls,
1323      char *const *args,
1324      const char *cfgfile,
1325      const struct GNUNET_CONFIGURATION_Handle *cfg)
1326 {
1327   (void) cls;
1328   (void) args;
1329   (void) cfgfile;
1330   if (NULL != args[0])
1331     GNUNET_log (
1332       GNUNET_ERROR_TYPE_WARNING,
1333       _ ("Superfluous command line arguments (starting with `%s') ignored\n"),
1334       args[0]);
1335   if ((NULL != args[0]) && (NULL == uri))
1336     uri = GNUNET_strdup (args[0]);
1337
1338   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, (void *) cfg);
1339
1340   if (NULL == ego_name)
1341   {
1342     idh = GNUNET_IDENTITY_connect (cfg, &id_connect_cb, (void *) cfg);
1343     if (NULL == idh)
1344       fprintf (stderr, _ ("Cannot connect to identity service\n"));
1345     ret = -1;
1346     return;
1347   }
1348   el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &identity_cb, (void *) cfg);
1349 }
1350
1351
1352 /**
1353  * Command-line option parser function that allows the user to specify
1354  * a complete record as one argument for adding/removing.  A pointer
1355  * to the head of the list of record sets must be passed as the "scls"
1356  * argument.
1357  *
1358  * @param ctx command line processor context
1359  * @param scls must be of type "struct GNUNET_FS_Uri **"
1360  * @param option name of the option (typically 'R')
1361  * @param value command line argument given; format is
1362  *        "TTL TYPE FLAGS VALUE" where TTL is an expiration time (rel or abs),
1363  *        always given in seconds (without the unit),
1364  *         TYPE is a DNS/GNS record type, FLAGS is either "n" for no flags or
1365  *         a combination of 's' (shadow) and 'p' (public) and VALUE is the
1366  *         value (in human-readable format)
1367  * @return #GNUNET_OK on success
1368  */
1369 static int
1370 multirecord_process (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
1371                      void *scls,
1372                      const char *option,
1373                      const char *value)
1374 {
1375   struct RecordSetEntry **head = scls;
1376   struct RecordSetEntry *r;
1377   struct GNUNET_GNSRECORD_Data record;
1378   char *cp;
1379   char *tok;
1380   char *saveptr;
1381   int etime_is_rel;
1382   void *raw_data;
1383
1384   (void) ctx;
1385   (void) option;
1386   cp = GNUNET_strdup (value);
1387   tok = strtok_r (cp, " ", &saveptr);
1388   if (NULL == tok)
1389   {
1390     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1391                 _ ("Empty record line argument is not allowed.\n"));
1392     GNUNET_free (cp);
1393     return GNUNET_SYSERR;
1394   }
1395   {
1396     char *etime_in_s;
1397
1398     GNUNET_asprintf (&etime_in_s, "%s s", tok);
1399     if (GNUNET_OK !=
1400         parse_expiration (etime_in_s, &etime_is_rel, &record.expiration_time))
1401     {
1402       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1403                   _ ("Invalid expiration time `%s' (must be without unit)\n"),
1404                   tok);
1405       GNUNET_free (cp);
1406       GNUNET_free (etime_in_s);
1407       return GNUNET_SYSERR;
1408     }
1409     GNUNET_free (etime_in_s);
1410   }
1411   tok = strtok_r (NULL, " ", &saveptr);
1412   if (NULL == tok)
1413   {
1414     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1415                 _ ("Missing entries in record line `%s'.\n"),
1416                 value);
1417     GNUNET_free (cp);
1418     return GNUNET_SYSERR;
1419   }
1420   record.record_type = GNUNET_GNSRECORD_typename_to_number (tok);
1421   if (UINT32_MAX == record.record_type)
1422   {
1423     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Unknown record type `%s'\n"), tok);
1424     GNUNET_free (cp);
1425     return GNUNET_SYSERR;
1426   }
1427   tok = strtok_r (NULL, " ", &saveptr);
1428   if (NULL == tok)
1429   {
1430     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1431                 _ ("Missing entries in record line `%s'.\n"),
1432                 value);
1433     GNUNET_free (cp);
1434     return GNUNET_SYSERR;
1435   }
1436   record.flags = GNUNET_GNSRECORD_RF_NONE;
1437   if (etime_is_rel)
1438     record.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1439   if (NULL == strchr (tok, (unsigned char) 'p')) /* p = public */
1440     record.flags |= GNUNET_GNSRECORD_RF_PRIVATE;
1441   if (NULL != strchr (tok, (unsigned char) 's'))
1442     record.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
1443   /* find beginning of record value */
1444   tok = strchr (&value[tok - cp], (unsigned char) ' ');
1445   if (NULL == tok)
1446   {
1447     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1448                 _ ("Missing entries in record line `%s'.\n"),
1449                 value);
1450     GNUNET_free (cp);
1451     return GNUNET_SYSERR;
1452   }
1453   GNUNET_free (cp);
1454   tok++; /* skip space */
1455   if (GNUNET_OK != GNUNET_GNSRECORD_string_to_value (record.record_type,
1456                                                      tok,
1457                                                      &raw_data,
1458                                                      &record.data_size))
1459   {
1460     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1461                 _ ("Invalid record data for type %s: `%s'.\n"),
1462                 GNUNET_GNSRECORD_number_to_typename (record.record_type),
1463                 tok);
1464     return GNUNET_SYSERR;
1465   }
1466
1467   r = GNUNET_malloc (sizeof (struct RecordSetEntry) + record.data_size);
1468   r->next = *head;
1469   record.data = &r[1];
1470   memcpy (&r[1], raw_data, record.data_size);
1471   GNUNET_free (raw_data);
1472   r->record = record;
1473   *head = r;
1474   return GNUNET_OK;
1475 }
1476
1477
1478 /**
1479  * Allow user to specify keywords.
1480  *
1481  * @param shortName short name of the option
1482  * @param name long name of the option
1483  * @param argumentHelp help text for the option argument
1484  * @param description long help text for the option
1485  * @param[out] topKeywords set to the desired value
1486  */
1487 struct GNUNET_GETOPT_CommandLineOption
1488 multirecord_option (char shortName,
1489                     const char *name,
1490                     const char *argumentHelp,
1491                     const char *description,
1492                     struct RecordSetEntry **rs)
1493 {
1494   struct GNUNET_GETOPT_CommandLineOption clo = {.shortName = shortName,
1495                                                 .name = name,
1496                                                 .argumentHelp = argumentHelp,
1497                                                 .description = description,
1498                                                 .require_argument = 1,
1499                                                 .processor =
1500                                                   &multirecord_process,
1501                                                 .scls = (void *) rs};
1502
1503   return clo;
1504 }
1505
1506
1507 /**
1508  * The main function for gnunet-namestore.
1509  *
1510  * @param argc number of arguments from the command line
1511  * @param argv command line arguments
1512  * @return 0 ok, 1 on error
1513  */
1514 int
1515 main (int argc, char *const *argv)
1516 {
1517   struct GNUNET_GETOPT_CommandLineOption options[] =
1518     {GNUNET_GETOPT_option_flag ('a', "add", gettext_noop ("add record"), &add),
1519      GNUNET_GETOPT_option_flag ('d',
1520                                 "delete",
1521                                 gettext_noop ("delete record"),
1522                                 &del),
1523      GNUNET_GETOPT_option_flag ('D',
1524                                 "display",
1525                                 gettext_noop ("display records"),
1526                                 &list),
1527      GNUNET_GETOPT_option_string (
1528        'e',
1529        "expiration",
1530        "TIME",
1531        gettext_noop (
1532          "expiration time for record to use (for adding only), \"never\" is possible"),
1533        &expirationstring),
1534      GNUNET_GETOPT_option_string ('i',
1535                                   "nick",
1536                                   "NICKNAME",
1537                                   gettext_noop (
1538                                     "set the desired nick name for the zone"),
1539                                   &nickstring),
1540      GNUNET_GETOPT_option_flag ('m',
1541                                 "monitor",
1542                                 gettext_noop (
1543                                   "monitor changes in the namestore"),
1544                                 &monitor),
1545      GNUNET_GETOPT_option_string ('n',
1546                                   "name",
1547                                   "NAME",
1548                                   gettext_noop (
1549                                     "name of the record to add/delete/display"),
1550                                   &name),
1551      GNUNET_GETOPT_option_string ('r',
1552                                   "reverse",
1553                                   "PKEY",
1554                                   gettext_noop (
1555                                     "determine our name for the given PKEY"),
1556                                   &reverse_pkey),
1557      multirecord_option (
1558        'R',
1559        "replace",
1560        "RECORDLINE",
1561        gettext_noop (
1562          "set record set to values given by (possibly multiple) RECORDLINES; can be specified multiple times"),
1563        &recordset),
1564      GNUNET_GETOPT_option_string ('t',
1565                                   "type",
1566                                   "TYPE",
1567                                   gettext_noop (
1568                                     "type of the record to add/delete/display"),
1569                                   &typestring),
1570      GNUNET_GETOPT_option_string ('u',
1571                                   "uri",
1572                                   "URI",
1573                                   gettext_noop ("URI to import into our zone"),
1574                                   &uri),
1575      GNUNET_GETOPT_option_string ('V',
1576                                   "value",
1577                                   "VALUE",
1578                                   gettext_noop (
1579                                     "value of the record to add/delete"),
1580                                   &value),
1581      GNUNET_GETOPT_option_flag ('p',
1582                                 "public",
1583                                 gettext_noop ("create or list public record"),
1584                                 &is_public),
1585      GNUNET_GETOPT_option_flag (
1586        's',
1587        "shadow",
1588        gettext_noop (
1589          "create shadow record (only valid if all other records of the same type have expired"),
1590        &is_shadow),
1591      GNUNET_GETOPT_option_string ('z',
1592                                   "zone",
1593                                   "EGO",
1594                                   gettext_noop (
1595                                     "name of the ego controlling the zone"),
1596                                   &ego_name),
1597      GNUNET_GETOPT_OPTION_END};
1598
1599   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1600     return 2;
1601
1602   is_public = -1;
1603   is_shadow = -1;
1604   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
1605   if (GNUNET_OK != GNUNET_PROGRAM_run (argc,
1606                                        argv,
1607                                        "gnunet-namestore",
1608                                        _ ("GNUnet zone manipulation tool"),
1609                                        options,
1610                                        &run,
1611                                        NULL))
1612   {
1613     GNUNET_free ((void *) argv);
1614     GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
1615     return 1;
1616   }
1617   GNUNET_free ((void *) argv);
1618   GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
1619   return ret;
1620 }
1621
1622 /* end of gnunet-namestore.c */