gnunet-gns.c 9.5 KB

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