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