namestore api change: include block expiration time in record create
[oweals/gnunet.git] / src / namestore / gnunet-namestore.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file gnunet-namestore.c
22  * @brief command line tool to manipulate the local zone
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - printing records
27  * - allow users to set record options (not just 'RF_AUTHORITY')
28  * - test
29  * - parsing SOA, PTR and MX value specifications (and define format!)
30  * - add options to list/lookup individual records
31  */
32 #include "platform.h"
33 #include <gnunet_util_lib.h>
34 #include <gnunet_dnsparser_lib.h>
35 #include <gnunet_namestore_service.h>
36
37 /**
38  * Handle to the namestore.
39  */
40 static struct GNUNET_NAMESTORE_Handle *ns;
41
42 /**
43  * Hash of the public key of our zone.
44  */
45 static GNUNET_HashCode zone;
46
47 /**
48  * Private key for the our zone.
49  */
50 static struct GNUNET_CRYPTO_RsaPrivateKey *zone_pkey;
51
52 /**
53  * Keyfile to manipulate.
54  */
55 static char *keyfile;   
56
57 /**
58  * Desired action is to add a record.
59  */
60 static int add;
61
62 /**
63  * Queue entry for the 'add' operation.
64  */
65 static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
66
67 /**
68  * Desired action is to list records.
69  */
70 static int list;
71
72 /**
73  * List iterator for the 'list' operation.
74  */
75 static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
76
77 /**
78  * Desired action is to remove a record.
79  */
80 static int del;
81
82 /**
83  * Queue entry for the 'del' operation.
84  */
85 static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
86
87 /**
88  * Name of the records to add/list/remove.
89  */
90 static char *name;
91
92 /**
93  * Value of the record to add/remove.
94  */
95 static char *value;
96
97 /**
98  * Type of the record to add/remove, NULL to remove all.
99  */
100 static char *typestring;
101
102 /**
103  * Desired expiration time.
104  */
105 static char *expirationstring;
106
107 /**
108  * Desired block expiration time.
109  */
110 static char *blockexpirationstring;
111
112 /**
113  * Task run on shutdown.  Cleans up everything.
114  *
115  * @param cls unused
116  * @param tc scheduler context
117  */
118 static void
119 do_shutdown (void *cls,
120              const struct GNUNET_SCHEDULER_TaskContext *tc)
121 {
122   if (NULL != ns)
123   {
124     GNUNET_NAMESTORE_disconnect (ns, GNUNET_NO);
125     ns = NULL;
126   }
127   if (NULL != zone_pkey)
128   {
129     GNUNET_CRYPTO_rsa_key_free (zone_pkey);
130     zone_pkey = NULL;
131   }
132 }
133
134
135 /**
136  * Continuation called to notify client about result of the
137  * operation.
138  *
139  * @param cls closure, unused
140  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
141  *                GNUNET_NO if content was already there
142  *                GNUNET_YES (or other positive value) on success
143  * @param emsg NULL on success, otherwise an error message
144  */
145 static void
146 add_continuation (void *cls,
147                   int32_t success,
148                   const char *emsg)
149 {
150   add_qe = NULL;
151   if (success != GNUNET_YES)
152     fprintf (stderr,
153              _("Adding record failed: %s\n"),
154              (success == GNUNET_NO) ? "record exists" : emsg);
155   if ( (NULL == del_qe) &&
156        (NULL == list_it) )
157     GNUNET_SCHEDULER_shutdown ();
158 }
159
160
161 /**
162  * Continuation called to notify client about result of the
163  * operation.
164  *
165  * @param cls closure, unused
166  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
167  *                GNUNET_NO if content was already there
168  *                GNUNET_YES (or other positive value) on success
169  * @param emsg NULL on success, otherwise an error message
170  */
171 static void
172 del_continuation (void *cls,
173                   int32_t success,
174                   const char *emsg)
175 {
176   del_qe = NULL;
177   if (success != GNUNET_YES)
178     fprintf (stderr,
179              _("Deleting record failed: %s\n"),
180              emsg);
181   if ( (NULL == add_qe) &&
182        (NULL == list_it) )
183     GNUNET_SCHEDULER_shutdown ();
184 }
185
186
187 /**
188  * Process a record that was stored in the namestore.
189  *
190  * @param cls closure
191  * @param zone_key public key of the zone
192  * @param expire when does the corresponding block in the DHT expire (until
193  *               when should we never do a DHT lookup for the same name again)?; 
194  *               GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
195  *               or the expiration time of the block in the namestore (even if there are zero
196  *               records matching the desired record type)
197  * @param name name that is being mapped (at most 255 characters long)
198  * @param rd_len number of entries in 'rd' array
199  * @param rd array of records with data to store
200  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
201  *        because the user queried for a particular record type only)
202  */
203 static void
204 display_record (void *cls,
205                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
206                 struct GNUNET_TIME_Absolute expire,                         
207                 const char *name,
208                 unsigned int rd_len,
209                 const struct GNUNET_NAMESTORE_RecordData *rd,
210                 const struct GNUNET_CRYPTO_RsaSignature *signature)
211 {
212   const char *typestring;
213   char *s;
214   unsigned int i;
215
216   if (NULL == name)
217   {
218     list_it = NULL;
219     if ( (NULL == del_qe) &&
220          (NULL == add_qe) )
221       GNUNET_SCHEDULER_shutdown ();
222     return;
223   }
224   FPRINTF (stdout,
225            "%s:\n",
226            name);
227   for (i=0;i<rd_len;i++)
228   {
229     typestring = GNUNET_NAMESTORE_number_to_typename (rd[i].record_type);
230     s = GNUNET_NAMESTORE_value_to_string (rd[i].record_type,
231                                           rd[i].data,
232                                           rd[i].data_size);
233     if (NULL == s)
234     {
235       FPRINTF (stdout, _("\tCorrupt or unsupported record of type %u\n"),
236                (unsigned int) rd[i].record_type);
237       continue;
238     }
239     FPRINTF (stdout, "\t%s: %s\n", typestring, s);
240     GNUNET_free (s);    
241   }
242   FPRINTF (stdout, "%s", "\n");
243   GNUNET_NAMESTORE_zone_iterator_next (list_it);
244 }
245
246
247 /**
248  * Main function that will be run.
249  *
250  * @param cls closure
251  * @param args remaining command-line arguments
252  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
253  * @param cfg configuration
254  */
255 static void
256 run (void *cls, char *const *args, const char *cfgfile,
257      const struct GNUNET_CONFIGURATION_Handle *cfg)
258 {
259   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
260   uint32_t type;
261   void *data = NULL;
262   size_t data_size = 0;
263   struct GNUNET_TIME_Relative etime;
264   struct GNUNET_TIME_Relative btime;
265   struct GNUNET_NAMESTORE_RecordData rd;
266
267   if (NULL == keyfile)
268   {
269     fprintf (stderr,
270              _("Option `%s' not given, but I need a zone key file!\n"),
271              "z");
272     return;
273   }
274   zone_pkey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
275   GNUNET_free (keyfile);
276   keyfile = NULL;
277   if (! (add|del|list))
278   {
279     /* nothing more to be done */  
280     GNUNET_CRYPTO_rsa_key_free (zone_pkey);
281     zone_pkey = NULL;
282     return; 
283   }
284   if (NULL == zone_pkey)
285   {
286     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
287                 _("Failed to read or create private zone key\n"));
288     return;
289   }
290   GNUNET_CRYPTO_rsa_key_get_public (zone_pkey,
291                                     &pub);
292   GNUNET_CRYPTO_hash (&pub, sizeof (pub), &zone);
293
294   ns = GNUNET_NAMESTORE_connect (cfg);
295   if (NULL == ns)
296   {
297     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
298                 _("Failed to connect to namestore\n"));
299     return;
300   }
301   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
302                                 &do_shutdown, NULL);
303   if (NULL == typestring)
304     type = 0;
305   else
306     type = GNUNET_NAMESTORE_typename_to_number (typestring);
307   if (UINT32_MAX == type)
308   {
309     fprintf (stderr, _("Unsupported type `%s'\n"), typestring);
310     GNUNET_SCHEDULER_shutdown ();
311     return;
312   } else if (add | del)
313   {
314     fprintf (stderr,
315              _("Missing option `%s' for operation `%s'\n"),
316              "-t", _("add/del"));
317     GNUNET_SCHEDULER_shutdown ();
318     return;     
319   }
320   if (NULL != value)
321   {
322     if (GNUNET_OK !=
323         GNUNET_NAMESTORE_string_to_value (type,
324                                           value,
325                                           &data,
326                                           &data_size))
327       {
328         fprintf (stderr, _("Value `%s' invalid for record type `%s'\n"), 
329                  value,
330                  typestring);
331         GNUNET_SCHEDULER_shutdown ();
332         return;
333       }
334   } else if (add | del)
335   {
336     fprintf (stderr,
337              _("Missing option `%s' for operation `%s'\n"),
338              "-V", _("add/del"));
339     GNUNET_SCHEDULER_shutdown ();
340     return;     
341   }
342   if (NULL != expirationstring)
343   {
344     if (GNUNET_OK !=
345         GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
346                                                &etime))
347     {
348       fprintf (stderr,
349                _("Invalid time format `%s'\n"),
350                expirationstring);
351       GNUNET_SCHEDULER_shutdown ();
352       return;     
353     }
354   } else if (add | del)
355   {
356     fprintf (stderr,
357              _("Missing option `%s' for operation `%s'\n"),
358              "-e", _("add/del"));
359     GNUNET_SCHEDULER_shutdown ();
360     return;     
361   }
362   if (NULL != blockexpirationstring)
363   {
364     if (GNUNET_OK !=
365         GNUNET_STRINGS_fancy_time_to_relative (blockexpirationstring,
366                                                &btime))
367     {
368       fprintf (stderr,
369                _("Invalid time format `%s'\n"),
370                blockexpirationstring);
371       GNUNET_SCHEDULER_shutdown ();
372       return;
373     }
374   } else if (add | del)
375   {
376     fprintf (stderr,
377              _("Missing option `%s' for operation `%s'\n"),
378              "-b", _("add/del"));
379     GNUNET_SCHEDULER_shutdown ();
380     return;
381   }
382
383   if (add)
384   {
385     if (NULL == name)
386     {
387       fprintf (stderr,
388                _("Missing option `%s' for operation `%s'\n"),
389                "-n", _("add"));
390       GNUNET_SCHEDULER_shutdown ();
391       return;     
392     }
393     rd.data = data;
394     rd.data_size = data_size;
395     rd.record_type = type;
396     rd.expiration = GNUNET_TIME_relative_to_absolute (etime);
397     rd.flags = GNUNET_NAMESTORE_RF_AUTHORITY; // FIXME: not always...
398     add_qe = GNUNET_NAMESTORE_record_create (ns,
399                                              zone_pkey,
400                                              GNUNET_TIME_relative_to_absolute (btime),
401                                              name,
402                                              &rd,
403                                              &add_continuation,
404                                              NULL);
405   }
406   if (del)
407   {
408     if (NULL == name)
409     {
410       fprintf (stderr,
411                _("Missing option `%s' for operation `%s'\n"),
412                "-n", _("del"));
413       GNUNET_SCHEDULER_shutdown ();
414       return;     
415     }
416     rd.data = data;
417     rd.data_size = data_size;
418     rd.record_type = type;
419     rd.expiration = GNUNET_TIME_relative_to_absolute (etime);
420     rd.flags = GNUNET_NAMESTORE_RF_AUTHORITY; // FIXME: not always...
421     del_qe = GNUNET_NAMESTORE_record_remove (ns,
422                                              zone_pkey,
423                                              name,
424                                              &rd,
425                                              &del_continuation,
426                                              NULL);
427   }
428   if (list)
429   {
430     list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
431                                                      &zone,
432                                                      0, 0,
433                                                      &display_record,
434                                                      NULL);
435   }
436   GNUNET_free_non_null (data);
437 }
438
439
440 /**
441  * The main function for gnunet-namestore.
442  *
443  * @param argc number of arguments from the command line
444  * @param argv command line arguments
445  * @return 0 ok, 1 on error
446  */
447 int
448 main (int argc, char *const *argv)
449 {
450   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
451     {'a', "add", NULL,
452      gettext_noop ("add record"), 0,
453      &GNUNET_GETOPT_set_one, &add},
454     {'b', "name-expiration", "TIME",
455      gettext_noop ("expiration time for name to use (for adding only)"), 1,
456      &GNUNET_GETOPT_set_string, &blockexpirationstring},
457     {'d', "delete", NULL,
458      gettext_noop ("delete record"), 0,
459      &GNUNET_GETOPT_set_one, &del},   
460     {'D', "display", NULL,
461      gettext_noop ("display records"), 0,
462      &GNUNET_GETOPT_set_one, &list},   
463     {'e', "expiration", "TIME",
464      gettext_noop ("expiration time for record to use (for adding only)"), 1,
465      &GNUNET_GETOPT_set_string, &expirationstring},   
466     {'n', "name", "NAME",
467      gettext_noop ("name of the record to add/delete/display"), 1,
468      &GNUNET_GETOPT_set_string, &name},   
469     {'t', "type", "TYPE",
470      gettext_noop ("type of the record to add/delete/display"), 1,
471      &GNUNET_GETOPT_set_string, &typestring},   
472     {'V', "value", "VALUE",
473      gettext_noop ("value of the record to add/delete"), 1,
474      &GNUNET_GETOPT_set_string, &value},   
475     {'z', "zonekey", "FILENAME",
476      gettext_noop ("filename with the zone key"), 1,
477      &GNUNET_GETOPT_set_string, &keyfile},   
478     GNUNET_GETOPT_OPTION_END
479   };
480
481   int ret;
482
483   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
484   ret =
485       (GNUNET_OK ==
486        GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
487                            _("GNUnet zone manipulation tool"), 
488                            options,
489                            &run, NULL)) ? 0 : 1;
490
491   return ret;
492 }
493
494 /* end of gnunet-namestore.c */