3514f4fd9491899a67d410564f50c1e6b14fd7c6
[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
663     case GNUNET_GNSRECORD_TYPE_PKEY:
664       fprintf (
665         stderr,
666         _ (
667           "A %s record exists already under `%s', no other records can be added.\n"),
668         "PKEY",
669         rec_name);
670       ret = 1;
671       test_finished ();
672       return;
673
674     case GNUNET_DNSPARSER_TYPE_SOA:
675       if (GNUNET_DNSPARSER_TYPE_SOA == type)
676       {
677         fprintf (
678           stderr,
679           _ (
680             "A SOA record exists already under `%s', cannot add a second SOA to the same zone.\n"),
681           rec_name);
682         ret = 1;
683         test_finished ();
684         return;
685       }
686       break;
687     }
688   }
689   switch (type)
690   {
691   case GNUNET_DNSPARSER_TYPE_CNAME:
692     if (0 != rd_count)
693     {
694       fprintf (stderr,
695                _ (
696                  "Records already exist under `%s', cannot add `%s' record.\n"),
697                rec_name,
698                "CNAME");
699       ret = 1;
700       test_finished ();
701       return;
702     }
703     break;
704
705   case GNUNET_GNSRECORD_TYPE_PKEY:
706     if (0 != rd_count)
707     {
708       fprintf (stderr,
709                _ (
710                  "Records already exist under `%s', cannot add `%s' record.\n"),
711                rec_name,
712                "PKEY");
713       ret = 1;
714       test_finished ();
715       return;
716     }
717     break;
718
719   case GNUNET_GNSRECORD_TYPE_GNS2DNS:
720     for (unsigned int i = 0; i < rd_count; i++)
721       if (GNUNET_GNSRECORD_TYPE_GNS2DNS != rd[i].record_type)
722       {
723         fprintf (
724           stderr,
725           _ (
726             "Non-GNS2DNS records already exist under `%s', cannot add GNS2DNS record.\n"),
727           rec_name);
728         ret = 1;
729         test_finished ();
730         return;
731       }
732     break;
733   }
734   memset (rdn, 0, sizeof(struct GNUNET_GNSRECORD_Data));
735   GNUNET_memcpy (&rdn[1], rd, rd_count * sizeof(struct GNUNET_GNSRECORD_Data));
736   rde = &rdn[0];
737   rde->data = data;
738   rde->data_size = data_size;
739   rde->record_type = type;
740   if (1 == is_shadow)
741     rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
742   if (1 != is_public)
743     rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
744   rde->expiration_time = etime;
745   if (GNUNET_YES == etime_is_rel)
746     rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
747   else if (GNUNET_NO != etime_is_rel)
748     rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
749   GNUNET_assert (NULL != name);
750   add_qe = GNUNET_NAMESTORE_records_store (ns,
751                                            &zone_pkey,
752                                            name,
753                                            rd_count + 1,
754                                            rde,
755                                            &add_continuation,
756                                            &add_qe);
757 }
758
759
760 /**
761  * Function called if we encountered an error in zone-to-name.
762  */
763 static void
764 reverse_error_cb (void *cls)
765 {
766   (void) cls;
767   reverse_qe = NULL;
768   fprintf (stdout, "%s.zkey\n", reverse_pkey);
769 }
770
771
772 /**
773  * Function called with the result of our attempt to obtain a name for a given
774  * public key.
775  *
776  * @param cls NULL
777  * @param zone private key of the zone; NULL on disconnect
778  * @param label label of the records; NULL on disconnect
779  * @param rd_count number of entries in @a rd array, 0 if label was deleted
780  * @param rd array of records with data to store
781  */
782 static void
783 handle_reverse_lookup (void *cls,
784                        const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
785                        const char *label,
786                        unsigned int rd_count,
787                        const struct GNUNET_GNSRECORD_Data *rd)
788 {
789   (void) cls;
790   (void) zone;
791   (void) rd_count;
792   (void) rd;
793   reverse_qe = NULL;
794   if (NULL == label)
795     fprintf (stdout, "%s\n", reverse_pkey);
796   else
797     fprintf (stdout, "%s.%s\n", label, ego_name);
798   test_finished ();
799 }
800
801
802 /**
803  * Function called if lookup for deletion fails.
804  */
805 static void
806 del_lookup_error_cb (void *cls)
807 {
808   (void) cls;
809   del_qe = NULL;
810   GNUNET_break (0);
811   ret = 1;
812   test_finished ();
813 }
814
815
816 /**
817  * We were asked to delete something; this function is called with
818  * the existing records. Now we should determine what should be
819  * deleted and then issue the deletion operation.
820  *
821  * @param cls NULL
822  * @param zone private key of the zone we are deleting from
823  * @param label name of the records we are editing
824  * @param rd_count size of the @a rd array
825  * @param rd existing records
826  */
827 static void
828 del_monitor (void *cls,
829              const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
830              const char *label,
831              unsigned int rd_count,
832              const struct GNUNET_GNSRECORD_Data *rd)
833 {
834   struct GNUNET_GNSRECORD_Data rdx[rd_count];
835   unsigned int rd_left;
836   uint32_t type;
837   char *vs;
838
839   (void) cls;
840   (void) zone;
841   del_qe = NULL;
842   if (0 == rd_count)
843   {
844     fprintf (stderr,
845              _ (
846                "There are no records under label `%s' that could be deleted.\n"),
847              label);
848     ret = 1;
849     test_finished ();
850     return;
851   }
852   if ((NULL == value) && (NULL == typestring))
853   {
854     /* delete everything */
855     del_qe = GNUNET_NAMESTORE_records_store (ns,
856                                              &zone_pkey,
857                                              name,
858                                              0,
859                                              NULL,
860                                              &del_continuation,
861                                              NULL);
862     return;
863   }
864   rd_left = 0;
865   if (NULL != typestring)
866     type = GNUNET_GNSRECORD_typename_to_number (typestring);
867   else
868     type = GNUNET_GNSRECORD_TYPE_ANY;
869   for (unsigned int i = 0; i < rd_count; i++)
870   {
871     vs = NULL;
872     if (! (((GNUNET_GNSRECORD_TYPE_ANY == type) ||
873             (rd[i].record_type == type)) &&
874            ((NULL == value) ||
875             (NULL ==
876              (vs = (GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
877                                                       rd[i].data,
878                                                       rd[i].data_size)))) ||
879             (0 == strcmp (vs, value)))))
880       rdx[rd_left++] = rd[i];
881     GNUNET_free_non_null (vs);
882   }
883   if (rd_count == rd_left)
884   {
885     /* nothing got deleted */
886     fprintf (
887       stderr,
888       _ (
889         "There are no records under label `%s' that match the request for deletion.\n"),
890       label);
891     test_finished ();
892     return;
893   }
894   /* delete everything but what we copied to 'rdx' */
895   del_qe = GNUNET_NAMESTORE_records_store (ns,
896                                            &zone_pkey,
897                                            name,
898                                            rd_left,
899                                            rdx,
900                                            &del_continuation,
901                                            NULL);
902 }
903
904
905 /**
906  * Parse expiration time.
907  *
908  * @param expirationstring text to parse
909  * @param etime_is_rel[out] set to #GNUNET_YES if time is relative
910  * @param etime[out] set to expiration time (abs or rel)
911  * @return #GNUNET_OK on success
912  */
913 static int
914 parse_expiration (const char *expirationstring,
915                   int *etime_is_rel,
916                   uint64_t *etime)
917 {
918   struct GNUNET_TIME_Relative etime_rel;
919   struct GNUNET_TIME_Absolute etime_abs;
920
921   if (0 == strcmp (expirationstring, "never"))
922   {
923     *etime = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
924     *etime_is_rel = GNUNET_NO;
925     return GNUNET_OK;
926   }
927   if (GNUNET_OK ==
928       GNUNET_STRINGS_fancy_time_to_relative (expirationstring, &etime_rel))
929   {
930     *etime_is_rel = GNUNET_YES;
931     *etime = etime_rel.rel_value_us;
932     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
933                 "Storing record with relative expiration time of %s\n",
934                 GNUNET_STRINGS_relative_time_to_string (etime_rel, GNUNET_NO));
935     return GNUNET_OK;
936   }
937   if (GNUNET_OK ==
938       GNUNET_STRINGS_fancy_time_to_absolute (expirationstring, &etime_abs))
939   {
940     *etime_is_rel = GNUNET_NO;
941     *etime = etime_abs.abs_value_us;
942     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
943                 "Storing record with absolute expiration time of %s\n",
944                 GNUNET_STRINGS_absolute_time_to_string (etime_abs));
945     return GNUNET_OK;
946   }
947   return GNUNET_SYSERR;
948 }
949
950
951 /**
952  * Function called when namestore is done with the replace
953  * operation.
954  *
955  * @param cls NULL
956  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
957  *                #GNUNET_NO if content was already there or not found
958  *                #GNUNET_YES (or other positive value) on success
959  * @param emsg NULL on success, otherwise an error message
960  */
961 static void
962 replace_cont (void *cls, int success, const char *emsg)
963 {
964   (void) cls;
965
966   set_qe = NULL;
967   if (GNUNET_OK != success)
968   {
969     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
970                 _ ("Failed to replace records: %s\n"),
971                 emsg);
972     ret = 1;   /* fail from 'main' */
973   }
974   GNUNET_SCHEDULER_shutdown ();
975 }
976
977
978 /**
979  * We have obtained the zone's private key, so now process
980  * the main commands using it.
981  *
982  * @param cfg configuration to use
983  */
984 static void
985 run_with_zone_pkey (const struct GNUNET_CONFIGURATION_Handle *cfg)
986 {
987   struct GNUNET_GNSRECORD_Data rd;
988
989   if (! (add | del | list | (NULL != nickstring) | (NULL != uri)
990          | (NULL != reverse_pkey) | (NULL != recordset)))
991   {
992     /* nothing more to be done */
993     fprintf (stderr, _ ("No options given\n"));
994     GNUNET_SCHEDULER_shutdown ();
995     return;
996   }
997   ns = GNUNET_NAMESTORE_connect (cfg);
998   if (NULL == ns)
999   {
1000     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1001                 _ ("Failed to connect to namestore\n"));
1002     return;
1003   }
1004
1005   if (NULL != recordset)
1006   {
1007     /* replace entire record set */
1008     unsigned int rd_count;
1009     struct GNUNET_GNSRECORD_Data *rd;
1010
1011     if (NULL == name)
1012     {
1013       fprintf (stderr,
1014                _ ("Missing option `%s' for operation `%s'\n"),
1015                "-R",
1016                _ ("replace"));
1017       GNUNET_SCHEDULER_shutdown ();
1018       ret = 1;
1019       return;
1020     }
1021     rd_count = 0;
1022     for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
1023       rd_count++;
1024     rd = GNUNET_new_array (rd_count, struct GNUNET_GNSRECORD_Data);
1025     rd_count = 0;
1026     for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
1027     {
1028       rd[rd_count] = e->record;
1029       rd_count++;
1030     }
1031     set_qe = GNUNET_NAMESTORE_records_store (ns,
1032                                              &zone_pkey,
1033                                              name,
1034                                              rd_count,
1035                                              rd,
1036                                              &replace_cont,
1037                                              NULL);
1038     GNUNET_free (rd);
1039     return;
1040   }
1041
1042   if (add)
1043   {
1044     if (NULL == name)
1045     {
1046       fprintf (stderr,
1047                _ ("Missing option `%s' for operation `%s'\n"),
1048                "-n",
1049                _ ("add"));
1050       GNUNET_SCHEDULER_shutdown ();
1051       ret = 1;
1052       return;
1053     }
1054     if (NULL == typestring)
1055     {
1056       fprintf (stderr,
1057                _ ("Missing option `%s' for operation `%s'\n"),
1058                "-t",
1059                _ ("add"));
1060       GNUNET_SCHEDULER_shutdown ();
1061       ret = 1;
1062       return;
1063     }
1064     type = GNUNET_GNSRECORD_typename_to_number (typestring);
1065     if (UINT32_MAX == type)
1066     {
1067       fprintf (stderr, _ ("Unsupported type `%s'\n"), typestring);
1068       GNUNET_SCHEDULER_shutdown ();
1069       ret = 1;
1070       return;
1071     }
1072     if (NULL == value)
1073     {
1074       fprintf (stderr,
1075                _ ("Missing option `%s' for operation `%s'\n"),
1076                "-V",
1077                _ ("add"));
1078       ret = 1;
1079       GNUNET_SCHEDULER_shutdown ();
1080       return;
1081     }
1082     if (GNUNET_OK !=
1083         GNUNET_GNSRECORD_string_to_value (type, value, &data, &data_size))
1084     {
1085       fprintf (stderr,
1086                _ ("Value `%s' invalid for record type `%s'\n"),
1087                value,
1088                typestring);
1089       GNUNET_SCHEDULER_shutdown ();
1090       ret = 1;
1091       return;
1092     }
1093     if (NULL == expirationstring)
1094     {
1095       fprintf (stderr,
1096                _ ("Missing option `%s' for operation `%s'\n"),
1097                "-e",
1098                _ ("add"));
1099       GNUNET_SCHEDULER_shutdown ();
1100       ret = 1;
1101       return;
1102     }
1103     if (GNUNET_OK != parse_expiration (expirationstring, &etime_is_rel, &etime))
1104     {
1105       fprintf (stderr, _ ("Invalid time format `%s'\n"), expirationstring);
1106       GNUNET_SCHEDULER_shutdown ();
1107       ret = 1;
1108       return;
1109     }
1110     add_qe = GNUNET_NAMESTORE_records_lookup (ns,
1111                                               &zone_pkey,
1112                                               name,
1113                                               &add_error_cb,
1114                                               NULL,
1115                                               &get_existing_record,
1116                                               NULL);
1117   }
1118   if (del)
1119   {
1120     if (NULL == name)
1121     {
1122       fprintf (stderr,
1123                _ ("Missing option `%s' for operation `%s'\n"),
1124                "-n",
1125                _ ("del"));
1126       GNUNET_SCHEDULER_shutdown ();
1127       ret = 1;
1128       return;
1129     }
1130     del_qe = GNUNET_NAMESTORE_records_lookup (ns,
1131                                               &zone_pkey,
1132                                               name,
1133                                               &del_lookup_error_cb,
1134                                               NULL,
1135                                               &del_monitor,
1136                                               NULL);
1137   }
1138   if (list)
1139   {
1140     if (NULL != name)
1141       get_qe = GNUNET_NAMESTORE_records_lookup (ns,
1142                                                 &zone_pkey,
1143                                                 name,
1144                                                 &lookup_error_cb,
1145                                                 NULL,
1146                                                 &display_record_lookup,
1147                                                 NULL);
1148     else
1149       list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
1150                                                        &zone_pkey,
1151                                                        &zone_iteration_error_cb,
1152                                                        NULL,
1153                                                        &display_record_iterator,
1154                                                        NULL,
1155                                                        &zone_iteration_finished,
1156                                                        NULL);
1157   }
1158   if (NULL != reverse_pkey)
1159   {
1160     struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
1161
1162     if (GNUNET_OK !=
1163         GNUNET_CRYPTO_ecdsa_public_key_from_string (reverse_pkey,
1164                                                     strlen (reverse_pkey),
1165                                                     &pubkey))
1166     {
1167       fprintf (stderr,
1168                _ ("Invalid public key for reverse lookup `%s'\n"),
1169                reverse_pkey);
1170       GNUNET_SCHEDULER_shutdown ();
1171     }
1172     reverse_qe = GNUNET_NAMESTORE_zone_to_name (ns,
1173                                                 &zone_pkey,
1174                                                 &pubkey,
1175                                                 &reverse_error_cb,
1176                                                 NULL,
1177                                                 &handle_reverse_lookup,
1178                                                 NULL);
1179   }
1180   if (NULL != uri)
1181   {
1182     char sh[105];
1183     char sname[64];
1184     struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1185
1186     GNUNET_STRINGS_utf8_tolower (uri, uri);
1187     if ((2 != (sscanf (uri, "gnunet://gns/%52s/%63s", sh, sname))) ||
1188         (GNUNET_OK !=
1189          GNUNET_CRYPTO_ecdsa_public_key_from_string (sh, strlen (sh), &pkey)))
1190     {
1191       fprintf (stderr, _ ("Invalid URI `%s'\n"), uri);
1192       GNUNET_SCHEDULER_shutdown ();
1193       ret = 1;
1194       return;
1195     }
1196     if (NULL == expirationstring)
1197     {
1198       fprintf (stderr,
1199                _ ("Missing option `%s' for operation `%s'\n"),
1200                "-e",
1201                _ ("add"));
1202       GNUNET_SCHEDULER_shutdown ();
1203       ret = 1;
1204       return;
1205     }
1206     if (GNUNET_OK != parse_expiration (expirationstring, &etime_is_rel, &etime))
1207     {
1208       fprintf (stderr, _ ("Invalid time format `%s'\n"), expirationstring);
1209       GNUNET_SCHEDULER_shutdown ();
1210       ret = 1;
1211       return;
1212     }
1213     memset (&rd, 0, sizeof(rd));
1214     rd.data = &pkey;
1215     rd.data_size = sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey);
1216     rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
1217     rd.expiration_time = etime;
1218     if (GNUNET_YES == etime_is_rel)
1219       rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1220     if (1 == is_shadow)
1221       rd.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
1222     add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
1223                                                  &zone_pkey,
1224                                                  sname,
1225                                                  1,
1226                                                  &rd,
1227                                                  &add_continuation,
1228                                                  &add_qe_uri);
1229   }
1230   if (NULL != nickstring)
1231   {
1232     if (0 == strlen (nickstring))
1233     {
1234       fprintf (stderr, _ ("Invalid nick `%s'\n"), nickstring);
1235       GNUNET_SCHEDULER_shutdown ();
1236       ret = 1;
1237       return;
1238     }
1239     add_qe_uri = GNUNET_NAMESTORE_set_nick (ns,
1240                                             &zone_pkey,
1241                                             nickstring,
1242                                             &add_continuation,
1243                                             &add_qe_uri);
1244   }
1245   if (monitor)
1246   {
1247     zm = GNUNET_NAMESTORE_zone_monitor_start (cfg,
1248                                               &zone_pkey,
1249                                               GNUNET_YES,
1250                                               &monitor_error_cb,
1251                                               NULL,
1252                                               &display_record_monitor,
1253                                               NULL,
1254                                               &sync_cb,
1255                                               NULL);
1256   }
1257 }
1258
1259
1260 /**
1261  * Callback invoked from identity service with ego information.
1262  * An @a ego of NULL means the ego was not found.
1263  *
1264  * @param cls closure with the configuration
1265  * @param ego an ego known to identity service, or NULL
1266  */
1267 static void
1268 identity_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego)
1269 {
1270   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1271
1272   el = NULL;
1273   if ((NULL != name) && (0 != strchr (name, '.')))
1274   {
1275     fprintf (stderr,
1276              _ ("Label `%s' contains `.' which is not allowed\n"),
1277              name);
1278     GNUNET_SCHEDULER_shutdown ();
1279     ret = -1;
1280     return;
1281   }
1282
1283   if (NULL == ego)
1284   {
1285     if (NULL != ego_name)
1286     {
1287       fprintf (stderr,
1288                _ ("Ego `%s' not known to identity service\n"),
1289                ego_name);
1290     }
1291     GNUNET_SCHEDULER_shutdown ();
1292     ret = -1;
1293     return;
1294   }
1295   zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
1296   GNUNET_free_non_null (ego_name);
1297   ego_name = NULL;
1298   run_with_zone_pkey (cfg);
1299 }
1300
1301
1302 /**
1303  * Function called with the default ego to be used for GNS
1304  * operations. Used if the user did not specify a zone via
1305  * command-line or environment variables.
1306  *
1307  * @param cls NULL
1308  * @param ego default ego, NULL for none
1309  * @param ctx NULL
1310  * @param name unused
1311  */
1312 static void
1313 default_ego_cb (void *cls,
1314                 struct GNUNET_IDENTITY_Ego *ego,
1315                 void **ctx,
1316                 const char *name)
1317 {
1318   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1319
1320   (void) ctx;
1321   (void) name;
1322   get_default = NULL;
1323   if (NULL == ego)
1324   {
1325     fprintf (stderr,
1326              _ ("No default identity configured for `namestore' subsystem\n"
1327                 "Run gnunet-identity -s namestore -e $NAME to set the default to $NAME\n"
1328                 "Run gnunet-identity -d to get a list of choices for $NAME\n"));
1329     GNUNET_SCHEDULER_shutdown ();
1330     ret = -1;
1331     return;
1332   }
1333   else
1334   {
1335     identity_cb ((void *) cfg, ego);
1336   }
1337 }
1338
1339
1340 /**
1341  * Function called with ALL of the egos known to the
1342  * identity service, used on startup if the user did
1343  * not specify a zone on the command-line.
1344  * Once the iteration is done (@a ego is NULL), we
1345  * ask for the default ego for "namestore".
1346  *
1347  * @param cls a `struct GNUNET_CONFIGURATION_Handle`
1348  * @param ego an ego, NULL for end of iteration
1349  * @param ctx NULL
1350  * @param name name associated with @a ego
1351  */
1352 static void
1353 id_connect_cb (void *cls,
1354                struct GNUNET_IDENTITY_Ego *ego,
1355                void **ctx,
1356                const char *name)
1357 {
1358   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1359
1360   (void) ctx;
1361   (void) name;
1362   if (NULL != ego)
1363     return;
1364   get_default =
1365     GNUNET_IDENTITY_get (idh, "namestore", &default_ego_cb, (void *) cfg);
1366 }
1367
1368
1369 /**
1370  * Main function that will be run.
1371  *
1372  * @param cls closure
1373  * @param args remaining command-line arguments
1374  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1375  * @param cfg configuration
1376  */
1377 static void
1378 run (void *cls,
1379      char *const *args,
1380      const char *cfgfile,
1381      const struct GNUNET_CONFIGURATION_Handle *cfg)
1382 {
1383   const char *pkey_str;
1384
1385   (void) cls;
1386   (void) args;
1387   (void) cfgfile;
1388   if (NULL != args[0])
1389     GNUNET_log (
1390       GNUNET_ERROR_TYPE_WARNING,
1391       _ ("Superfluous command line arguments (starting with `%s') ignored\n"),
1392       args[0]);
1393   if ((NULL != args[0]) && (NULL == uri))
1394     uri = GNUNET_strdup (args[0]);
1395
1396   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, (void *) cfg);
1397   pkey_str = getenv ("GNUNET_NAMESTORE_EGO_PRIVATE_KEY");
1398   if (NULL != pkey_str)
1399   {
1400     if (GNUNET_OK != GNUNET_STRINGS_string_to_data (pkey_str,
1401                                                     strlen (pkey_str),
1402                                                     &zone_pkey,
1403                                                     sizeof(zone_pkey)))
1404     {
1405       fprintf (stderr,
1406                "Malformed private key `%s' in $%s\n",
1407                pkey_str,
1408                "GNUNET_NAMESTORE_EGO_PRIVATE_KEY");
1409       ret = 1;
1410       GNUNET_SCHEDULER_shutdown ();
1411       return;
1412     }
1413     run_with_zone_pkey (cfg);
1414     return;
1415   }
1416   if (NULL == ego_name)
1417   {
1418     idh = GNUNET_IDENTITY_connect (cfg, &id_connect_cb, (void *) cfg);
1419     if (NULL == idh)
1420       fprintf (stderr, _ ("Cannot connect to identity service\n"));
1421     ret = -1;
1422     return;
1423   }
1424   el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &identity_cb, (void *) cfg);
1425 }
1426
1427
1428 /**
1429  * Command-line option parser function that allows the user to specify
1430  * a complete record as one argument for adding/removing.  A pointer
1431  * to the head of the list of record sets must be passed as the "scls"
1432  * argument.
1433  *
1434  * @param ctx command line processor context
1435  * @param scls must be of type "struct GNUNET_FS_Uri **"
1436  * @param option name of the option (typically 'R')
1437  * @param value command line argument given; format is
1438  *        "TTL TYPE FLAGS VALUE" where TTL is an expiration time (rel or abs),
1439  *        always given in seconds (without the unit),
1440  *         TYPE is a DNS/GNS record type, FLAGS is either "n" for no flags or
1441  *         a combination of 's' (shadow) and 'p' (public) and VALUE is the
1442  *         value (in human-readable format)
1443  * @return #GNUNET_OK on success
1444  */
1445 static int
1446 multirecord_process (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
1447                      void *scls,
1448                      const char *option,
1449                      const char *value)
1450 {
1451   struct RecordSetEntry **head = scls;
1452   struct RecordSetEntry *r;
1453   struct GNUNET_GNSRECORD_Data record;
1454   char *cp;
1455   char *tok;
1456   char *saveptr;
1457   int etime_is_rel;
1458   void *raw_data;
1459
1460   (void) ctx;
1461   (void) option;
1462   cp = GNUNET_strdup (value);
1463   tok = strtok_r (cp, " ", &saveptr);
1464   if (NULL == tok)
1465   {
1466     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1467                 _ ("Empty record line argument is not allowed.\n"));
1468     GNUNET_free (cp);
1469     return GNUNET_SYSERR;
1470   }
1471   {
1472     char *etime_in_s;
1473
1474     GNUNET_asprintf (&etime_in_s, "%s s", tok);
1475     if (GNUNET_OK !=
1476         parse_expiration (etime_in_s, &etime_is_rel, &record.expiration_time))
1477     {
1478       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1479                   _ ("Invalid expiration time `%s' (must be without unit)\n"),
1480                   tok);
1481       GNUNET_free (cp);
1482       GNUNET_free (etime_in_s);
1483       return GNUNET_SYSERR;
1484     }
1485     GNUNET_free (etime_in_s);
1486   }
1487   tok = strtok_r (NULL, " ", &saveptr);
1488   if (NULL == tok)
1489   {
1490     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1491                 _ ("Missing entries in record line `%s'.\n"),
1492                 value);
1493     GNUNET_free (cp);
1494     return GNUNET_SYSERR;
1495   }
1496   record.record_type = GNUNET_GNSRECORD_typename_to_number (tok);
1497   if (UINT32_MAX == record.record_type)
1498   {
1499     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Unknown record type `%s'\n"), tok);
1500     GNUNET_free (cp);
1501     return GNUNET_SYSERR;
1502   }
1503   tok = strtok_r (NULL, " ", &saveptr);
1504   if (NULL == tok)
1505   {
1506     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1507                 _ ("Missing entries in record line `%s'.\n"),
1508                 value);
1509     GNUNET_free (cp);
1510     return GNUNET_SYSERR;
1511   }
1512   record.flags = GNUNET_GNSRECORD_RF_NONE;
1513   if (etime_is_rel)
1514     record.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1515   if (NULL == strchr (tok, (unsigned char) 'p')) /* p = public */
1516     record.flags |= GNUNET_GNSRECORD_RF_PRIVATE;
1517   if (NULL != strchr (tok, (unsigned char) 's'))
1518     record.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
1519   /* find beginning of record value */
1520   tok = strchr (&value[tok - cp], (unsigned char) ' ');
1521   if (NULL == tok)
1522   {
1523     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1524                 _ ("Missing entries in record line `%s'.\n"),
1525                 value);
1526     GNUNET_free (cp);
1527     return GNUNET_SYSERR;
1528   }
1529   GNUNET_free (cp);
1530   tok++; /* skip space */
1531   if (GNUNET_OK != GNUNET_GNSRECORD_string_to_value (record.record_type,
1532                                                      tok,
1533                                                      &raw_data,
1534                                                      &record.data_size))
1535   {
1536     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1537                 _ ("Invalid record data for type %s: `%s'.\n"),
1538                 GNUNET_GNSRECORD_number_to_typename (record.record_type),
1539                 tok);
1540     return GNUNET_SYSERR;
1541   }
1542
1543   r = GNUNET_malloc (sizeof(struct RecordSetEntry) + record.data_size);
1544   r->next = *head;
1545   record.data = &r[1];
1546   memcpy (&r[1], raw_data, record.data_size);
1547   GNUNET_free (raw_data);
1548   r->record = record;
1549   *head = r;
1550   return GNUNET_OK;
1551 }
1552
1553
1554 /**
1555  * Allow user to specify keywords.
1556  *
1557  * @param shortName short name of the option
1558  * @param name long name of the option
1559  * @param argumentHelp help text for the option argument
1560  * @param description long help text for the option
1561  * @param[out] topKeywords set to the desired value
1562  */
1563 struct GNUNET_GETOPT_CommandLineOption
1564 multirecord_option (char shortName,
1565                     const char *name,
1566                     const char *argumentHelp,
1567                     const char *description,
1568                     struct RecordSetEntry **rs)
1569 {
1570   struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName,
1571                                                  .name = name,
1572                                                  .argumentHelp = argumentHelp,
1573                                                  .description = description,
1574                                                  .require_argument = 1,
1575                                                  .processor =
1576                                                    &multirecord_process,
1577                                                  .scls = (void *) rs };
1578
1579   return clo;
1580 }
1581
1582
1583 /**
1584  * The main function for gnunet-namestore.
1585  *
1586  * @param argc number of arguments from the command line
1587  * @param argv command line arguments
1588  * @return 0 ok, 1 on error
1589  */
1590 int
1591 main (int argc, char *const *argv)
1592 {
1593   struct GNUNET_GETOPT_CommandLineOption options[] =
1594   { GNUNET_GETOPT_option_flag ('a', "add", gettext_noop ("add record"), &add),
1595     GNUNET_GETOPT_option_flag ('d',
1596                                "delete",
1597                                gettext_noop ("delete record"),
1598                                &del),
1599     GNUNET_GETOPT_option_flag ('D',
1600                                "display",
1601                                gettext_noop ("display records"),
1602                                &list),
1603     GNUNET_GETOPT_option_string (
1604       'e',
1605       "expiration",
1606       "TIME",
1607       gettext_noop (
1608         "expiration time for record to use (for adding only), \"never\" is possible"),
1609       &expirationstring),
1610     GNUNET_GETOPT_option_string ('i',
1611                                  "nick",
1612                                  "NICKNAME",
1613                                  gettext_noop (
1614                                    "set the desired nick name for the zone"),
1615                                  &nickstring),
1616     GNUNET_GETOPT_option_flag ('m',
1617                                "monitor",
1618                                gettext_noop (
1619                                  "monitor changes in the namestore"),
1620                                &monitor),
1621     GNUNET_GETOPT_option_string ('n',
1622                                  "name",
1623                                  "NAME",
1624                                  gettext_noop (
1625                                    "name of the record to add/delete/display"),
1626                                  &name),
1627     GNUNET_GETOPT_option_string ('r',
1628                                  "reverse",
1629                                  "PKEY",
1630                                  gettext_noop (
1631                                    "determine our name for the given PKEY"),
1632                                  &reverse_pkey),
1633     multirecord_option (
1634       'R',
1635       "replace",
1636       "RECORDLINE",
1637       gettext_noop (
1638         "set record set to values given by (possibly multiple) RECORDLINES; can be specified multiple times"),
1639       &recordset),
1640     GNUNET_GETOPT_option_string ('t',
1641                                  "type",
1642                                  "TYPE",
1643                                  gettext_noop (
1644                                    "type of the record to add/delete/display"),
1645                                  &typestring),
1646     GNUNET_GETOPT_option_string ('u',
1647                                  "uri",
1648                                  "URI",
1649                                  gettext_noop ("URI to import into our zone"),
1650                                  &uri),
1651     GNUNET_GETOPT_option_string ('V',
1652                                  "value",
1653                                  "VALUE",
1654                                  gettext_noop (
1655                                    "value of the record to add/delete"),
1656                                  &value),
1657     GNUNET_GETOPT_option_flag ('p',
1658                                "public",
1659                                gettext_noop ("create or list public record"),
1660                                &is_public),
1661     GNUNET_GETOPT_option_flag (
1662       's',
1663       "shadow",
1664       gettext_noop (
1665         "create shadow record (only valid if all other records of the same type have expired"),
1666       &is_shadow),
1667     GNUNET_GETOPT_option_string ('z',
1668                                  "zone",
1669                                  "EGO",
1670                                  gettext_noop (
1671                                    "name of the ego controlling the zone"),
1672                                  &ego_name),
1673     GNUNET_GETOPT_OPTION_END };
1674   int lret;
1675
1676   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1677     return 2;
1678
1679   is_public = -1;
1680   is_shadow = -1;
1681   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
1682   if (GNUNET_OK !=
1683       (lret = GNUNET_PROGRAM_run (argc,
1684                                   argv,
1685                                   "gnunet-namestore",
1686                                   _ ("GNUnet zone manipulation tool"),
1687                                   options,
1688                                   &run,
1689                                   NULL)))
1690   {
1691     GNUNET_free_nz ((void *) argv);
1692     GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
1693     return lret;
1694   }
1695   GNUNET_free_nz ((void *) argv);
1696   GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
1697   return ret;
1698 }
1699
1700
1701 /* end of gnunet-namestore.c */