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