remove 'illegal' (non-reentrant) log logic from signal handler
[oweals/gnunet.git] / src / gns / gnunet-gns.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012-2013, 2017-2018 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-gns.c
22  * @brief command line tool to access distributed GNS
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #if HAVE_LIBIDN2
27 #if HAVE_IDN2_H
28 #include <idn2.h>
29 #elif HAVE_IDN2_IDN2_H
30 #include <idn2/idn2.h>
31 #endif
32 #elif HAVE_LIBIDN
33 #if HAVE_IDNA_H
34 #include <idna.h>
35 #elif HAVE_IDN_IDNA_H
36 #include <idn/idna.h>
37 #endif
38 #endif
39 #include <gnunet_util_lib.h>
40 #include <gnunet_dnsparser_lib.h>
41 #include <gnunet_gnsrecord_lib.h>
42 #include <gnunet_namestore_service.h>
43 #include <gnunet_gns_service.h>
44
45
46 /**
47  * Configuration we are using.
48  */
49 static const struct GNUNET_CONFIGURATION_Handle *cfg;
50
51 /**
52  * Handle to GNS service.
53  */
54 static struct GNUNET_GNS_Handle *gns;
55
56 /**
57  * GNS name to lookup. (-u option)
58  */
59 static char *lookup_name;
60
61 /**
62  * DNS IDNA name to lookup. (set if -d option is set)
63  */
64 char *idna_name;
65
66 /**
67  * DNS compatibility (name is given as DNS name, possible IDNA).
68  */
69 static int dns_compat;
70
71 /**
72  * record type to look up (-t option)
73  */
74 static char *lookup_type;
75
76 /**
77  * raw output
78  */
79 static int raw;
80
81 /**
82  * Desired record type.
83  */
84 static uint32_t rtype;
85
86 /**
87  * Timeout for lookup
88  */
89 static struct GNUNET_TIME_Relative timeout;
90
91 /**
92  * Timeout task
93  */
94 static struct GNUNET_SCHEDULER_Task *to_task;
95
96 /**
97  * Handle to lookup request
98  */
99 static struct GNUNET_GNS_LookupWithTldRequest *lr;
100
101 /**
102  * Global return value.
103  * 0 on success (default),
104  * 1 on internal failures
105  * 2 on launch failure,
106  * 4 if the name is not a GNS-supported TLD,
107  */
108 static int global_ret;
109
110
111 /**
112  * Task run on shutdown.  Cleans up everything.
113  *
114  * @param cls unused
115  */
116 static void
117 do_shutdown (void *cls)
118 {
119   (void) cls;
120   if (NULL != to_task)
121   {
122     GNUNET_SCHEDULER_cancel (to_task);
123     to_task = NULL;
124   }
125   if (NULL != lr)
126   {
127     GNUNET_GNS_lookup_with_tld_cancel (lr);
128     lr = NULL;
129   }
130   if (NULL != gns)
131   {
132     GNUNET_GNS_disconnect (gns);
133     gns = NULL;
134   }
135   if (NULL != idna_name)
136   {
137     GNUNET_free (idna_name);
138     idna_name = NULL;
139   }
140 }
141
142
143 /**
144  * Task to run on timeout
145  *
146  * @param cls unused
147  */
148 static void
149 do_timeout (void*cls)
150 {
151   to_task = NULL;
152   global_ret = 3; // Timeout
153   GNUNET_SCHEDULER_shutdown ();
154 }
155
156
157 /**
158  * Function called with the result of a GNS lookup.
159  *
160  * @param cls the 'const char *' name that was resolved
161  * @param was_gns #GNUNET_NO if TLD did not indicate use of GNS
162  * @param rd_count number of records returned
163  * @param rd array of @a rd_count records with the results
164  */
165 static void
166 process_lookup_result (void *cls,
167                        int was_gns,
168                        uint32_t rd_count,
169                        const struct GNUNET_GNSRECORD_Data *rd)
170 {
171   const char *name = cls;
172   const char *typename;
173   char *string_val;
174
175   lr = NULL;
176   if (GNUNET_NO == was_gns)
177   {
178     global_ret = 4;   /* not for GNS */
179     GNUNET_SCHEDULER_shutdown ();
180     return;
181   }
182   if (! raw)
183   {
184     if (0 == rd_count)
185       printf ("No results.\n");
186     else
187       printf ("%s:\n", name);
188   }
189   for (uint32_t i = 0; i < rd_count; i++)
190   {
191     if ((rd[i].record_type != rtype) && (GNUNET_GNSRECORD_TYPE_ANY != rtype))
192       continue;
193     typename = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
194     string_val = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
195                                                    rd[i].data,
196                                                    rd[i].data_size);
197     if (NULL == string_val)
198     {
199       fprintf (stderr,
200                "Record %u of type %d malformed, skipping\n",
201                (unsigned int) i,
202                (int) rd[i].record_type);
203       continue;
204     }
205     if (raw)
206       printf ("%s\n", string_val);
207     else
208       printf ("Got `%s' record: %s%s\n",
209               typename,
210               string_val,
211               (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SUPPLEMENTAL)) ?
212               " (supplemental)" : "");
213     GNUNET_free (string_val);
214   }
215   GNUNET_SCHEDULER_shutdown ();
216 }
217
218
219 /**
220  * Main function that will be run.
221  *
222  * @param cls closure
223  * @param args remaining command-line arguments
224  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
225  * @param c configuration
226  */
227 static void
228 run (void *cls,
229      char *const *args,
230      const char *cfgfile,
231      const struct GNUNET_CONFIGURATION_Handle *c)
232 {
233   (void) cls;
234   (void) args;
235   (void) cfgfile;
236
237   cfg = c;
238   to_task = NULL;
239   {
240     char *colon;
241
242     if (NULL != (colon = strchr (lookup_name, ':')))
243       *colon = '\0';
244   }
245
246   /**
247    * If DNS compatibility is requested, we first verify that the
248    * lookup_name is in a DNS format. If yes, we convert it to UTF-8.
249    */
250   if (GNUNET_YES == dns_compat)
251   {
252     Idna_rc rc;
253
254     if (GNUNET_OK != GNUNET_DNSPARSER_check_name (lookup_name))
255     {
256       fprintf (stderr,
257                _ ("`%s' is not a valid DNS domain name\n"),
258                lookup_name);
259       global_ret = 3;
260       return;
261     }
262     if (IDNA_SUCCESS !=
263         (rc = idna_to_unicode_8z8z (lookup_name, &idna_name,
264                                     IDNA_ALLOW_UNASSIGNED)))
265     {
266       fprintf (stderr,
267                _ ("Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"),
268                lookup_name,
269                idna_strerror (rc));
270       global_ret = 4;
271       return;
272     }
273     lookup_name = idna_name;
274   }
275
276   if (GNUNET_YES !=
277       GNUNET_CLIENT_test (cfg,
278                           "arm"))
279   {
280     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
281                 _ ("Cannot resolve using GNS: GNUnet peer not running\n"));
282     global_ret = 2;
283     return;
284   }
285   to_task = GNUNET_SCHEDULER_add_delayed (timeout,
286                                           &do_timeout,
287                                           NULL);
288   gns = GNUNET_GNS_connect (cfg);
289   if (NULL == gns)
290   {
291     fprintf (stderr,
292              _ ("Failed to connect to GNS\n"));
293     global_ret = 2;
294     return;
295   }
296   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
297                                  NULL);
298   if (NULL != lookup_type)
299     rtype = GNUNET_GNSRECORD_typename_to_number (lookup_type);
300   else
301     rtype = GNUNET_DNSPARSER_TYPE_A;
302   if (UINT32_MAX == rtype)
303   {
304     fprintf (stderr,
305              _ ("Invalid typename specified, assuming `ANY'\n"));
306     rtype = GNUNET_GNSRECORD_TYPE_ANY;
307   }
308   lr = GNUNET_GNS_lookup_with_tld (gns,
309                                    lookup_name,
310                                    rtype,
311                                    GNUNET_GNS_LO_DEFAULT,
312                                    &process_lookup_result,
313                                    lookup_name);
314   if (NULL == lr)
315   {
316     global_ret = 2;
317     GNUNET_SCHEDULER_shutdown ();
318     return;
319   }
320 }
321
322
323 /**
324  * The main function for gnunet-gns.
325  *
326  * @param argc number of arguments from the command line
327  * @param argv command line arguments
328  * @return 0 ok, 1 on error
329  */
330 int
331 main (int argc, char *const *argv)
332 {
333   timeout = GNUNET_TIME_UNIT_FOREVER_REL;
334   struct GNUNET_GETOPT_CommandLineOption options[] =
335   { GNUNET_GETOPT_option_mandatory (
336       GNUNET_GETOPT_option_string ('u',
337                                    "lookup",
338                                    "NAME",
339                                    gettext_noop (
340                                      "Lookup a record for the given name"),
341                                    &lookup_name)),
342     GNUNET_GETOPT_option_string ('t',
343                                  "type",
344                                  "TYPE",
345                                  gettext_noop (
346                                    "Specify the type of the record to lookup"),
347                                  &lookup_type),
348     GNUNET_GETOPT_option_relative_time ('T',
349                                         "timeout",
350                                         "TIMEOUT",
351                                         gettext_noop (
352                                           "Specify a timeout for the lookup"),
353                                         &timeout),
354     GNUNET_GETOPT_option_flag ('r',
355                                "raw",
356                                gettext_noop ("No unneeded output"),
357                                &raw),
358     GNUNET_GETOPT_option_flag ('d',
359                                "dns",
360                                gettext_noop (
361                                  "DNS Compatibility: Name is passed in IDNA instead of UTF-8"),
362                                &dns_compat),
363     GNUNET_GETOPT_OPTION_END };
364   int ret;
365
366   if (GNUNET_OK !=
367       GNUNET_STRINGS_get_utf8_args (argc, argv,
368                                     &argc, &argv))
369     return 2;
370
371   GNUNET_log_setup ("gnunet-gns", "WARNING", NULL);
372   ret = GNUNET_PROGRAM_run (argc,
373                             argv,
374                             "gnunet-gns",
375                             _ ("GNUnet GNS resolver tool"),
376                             options,
377                             &run,
378                             NULL);
379   GNUNET_free ((void *) argv);
380   if (GNUNET_OK != ret)
381     return 1;
382   return global_ret;
383 }
384
385
386 /* end of gnunet-gns.c */