plugin_rest_gns.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2012-2015 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. * @author Philippe Buschmann
  18. * @file gns/plugin_rest_gns.c
  19. * @brief GNUnet Gns REST plugin
  20. */
  21. #include "platform.h"
  22. #include "gnunet_rest_plugin.h"
  23. #include "gnunet_rest_lib.h"
  24. #include "gnunet_json_lib.h"
  25. #include "gnunet_gnsrecord_lib.h"
  26. #include "gnunet_gns_service.h"
  27. #include "microhttpd.h"
  28. #include <jansson.h>
  29. /**
  30. * Rest API GNS Namespace
  31. */
  32. #define GNUNET_REST_API_NS_GNS "/gns"
  33. /**
  34. * Rest API GNS Parameter record_type
  35. */
  36. #define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type"
  37. /**
  38. * Rest API GNS ERROR Unknown Error
  39. */
  40. #define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error"
  41. /**
  42. * Rest API GNS ERROR Record not found
  43. */
  44. #define GNUNET_REST_GNS_NOT_FOUND "Record not found"
  45. /**
  46. * The configuration handle
  47. */
  48. const struct GNUNET_CONFIGURATION_Handle *cfg;
  49. /**
  50. * HTTP methods allows for this plugin
  51. */
  52. static char *allow_methods;
  53. /**
  54. * @brief struct returned by the initialization function of the plugin
  55. */
  56. struct Plugin
  57. {
  58. const struct GNUNET_CONFIGURATION_Handle *cfg;
  59. };
  60. /**
  61. * The request handle
  62. */
  63. struct RequestHandle
  64. {
  65. /**
  66. * Connection to GNS
  67. */
  68. struct GNUNET_GNS_Handle *gns;
  69. /**
  70. * Active GNS lookup
  71. */
  72. struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
  73. /**
  74. * Name to look up
  75. */
  76. char *name;
  77. /**
  78. * Record type to look up
  79. */
  80. int record_type;
  81. /**
  82. * Rest connection
  83. */
  84. struct GNUNET_REST_RequestHandle *rest_handle;
  85. /**
  86. * Desired timeout for the lookup (default is no timeout).
  87. */
  88. struct GNUNET_TIME_Relative timeout;
  89. /**
  90. * ID of a task associated with the resolution process.
  91. */
  92. struct GNUNET_SCHEDULER_Task *timeout_task;
  93. /**
  94. * The plugin result processor
  95. */
  96. GNUNET_REST_ResultProcessor proc;
  97. /**
  98. * The closure of the result processor
  99. */
  100. void *proc_cls;
  101. /**
  102. * The url
  103. */
  104. char *url;
  105. /**
  106. * Error response message
  107. */
  108. char *emsg;
  109. /**
  110. * Response code
  111. */
  112. int response_code;
  113. };
  114. /**
  115. * Cleanup lookup handle
  116. * @param handle Handle to clean up
  117. */
  118. static void
  119. cleanup_handle (void *cls)
  120. {
  121. struct RequestHandle *handle = cls;
  122. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
  123. if (NULL != handle->gns_lookup)
  124. {
  125. GNUNET_GNS_lookup_with_tld_cancel (handle->gns_lookup);
  126. handle->gns_lookup = NULL;
  127. }
  128. if (NULL != handle->gns)
  129. {
  130. GNUNET_GNS_disconnect (handle->gns);
  131. handle->gns = NULL;
  132. }
  133. if (NULL != handle->timeout_task)
  134. {
  135. GNUNET_SCHEDULER_cancel (handle->timeout_task);
  136. handle->timeout_task = NULL;
  137. }
  138. if (NULL != handle->url)
  139. GNUNET_free (handle->url);
  140. if (NULL != handle->name)
  141. GNUNET_free (handle->name);
  142. if (NULL != handle->emsg)
  143. GNUNET_free (handle->emsg);
  144. GNUNET_free (handle);
  145. }
  146. /**
  147. * Task run on errors. Reports an error and cleans up everything.
  148. *
  149. * @param cls the `struct RequestHandle`
  150. */
  151. static void
  152. do_error (void *cls)
  153. {
  154. struct RequestHandle *handle = cls;
  155. struct MHD_Response *resp;
  156. json_t *json_error = json_object ();
  157. char *response;
  158. if (NULL != handle->timeout_task)
  159. GNUNET_SCHEDULER_cancel (handle->timeout_task);
  160. handle->timeout_task = NULL;
  161. if (NULL == handle->emsg)
  162. handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_ERROR_UNKNOWN);
  163. json_object_set_new (json_error, "error", json_string (handle->emsg));
  164. if (0 == handle->response_code)
  165. handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
  166. response = json_dumps (json_error, 0);
  167. resp = GNUNET_REST_create_response (response);
  168. MHD_add_response_header (resp, "Content-Type", "application/json");
  169. handle->proc (handle->proc_cls, resp, handle->response_code);
  170. json_decref (json_error);
  171. GNUNET_free (response);
  172. GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
  173. }
  174. static void
  175. do_timeout (void *cls)
  176. {
  177. struct RequestHandle *handle = cls;
  178. handle->timeout_task = NULL;
  179. handle->response_code = MHD_HTTP_REQUEST_TIMEOUT;
  180. do_error (handle);
  181. }
  182. /**
  183. * Iterator called on obtained result for a GNS lookup.
  184. *
  185. * @param cls closure with the object
  186. * @param was_gns #GNUNET_NO if name was not a GNS name
  187. * @param rd_count number of records in @a rd
  188. * @param rd the records in reply
  189. */
  190. static void
  191. handle_gns_response (void *cls,
  192. int was_gns,
  193. uint32_t rd_count,
  194. const struct GNUNET_GNSRECORD_Data *rd)
  195. {
  196. struct RequestHandle *handle = cls;
  197. struct MHD_Response *resp;
  198. json_t *result_obj;
  199. char *result;
  200. handle->gns_lookup = NULL;
  201. if (GNUNET_NO == was_gns)
  202. {
  203. handle->response_code = MHD_HTTP_NOT_FOUND;
  204. handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
  205. GNUNET_SCHEDULER_add_now (&do_error, handle);
  206. return;
  207. }
  208. result_obj = GNUNET_JSON_from_gnsrecord (handle->name, rd, rd_count);
  209. result = json_dumps (result_obj, 0);
  210. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
  211. resp = GNUNET_REST_create_response (result);
  212. MHD_add_response_header (resp, "Content-Type", "application/json");
  213. handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
  214. GNUNET_free (result);
  215. json_decref (result_obj);
  216. GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
  217. }
  218. /**
  219. * Handle gns GET request
  220. *
  221. * @param con_handle the connection handle
  222. * @param url the url
  223. * @param cls the RequestHandle
  224. */
  225. void
  226. get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle,
  227. const char *url,
  228. void *cls)
  229. {
  230. struct RequestHandle *handle = cls;
  231. struct GNUNET_HashCode key;
  232. char *record_type;
  233. char *name;
  234. name = NULL;
  235. handle->name = NULL;
  236. if (strlen (GNUNET_REST_API_NS_GNS) < strlen (handle->url))
  237. {
  238. name = &handle->url[strlen (GNUNET_REST_API_NS_GNS) + 1];
  239. }
  240. if (NULL == name)
  241. {
  242. handle->response_code = MHD_HTTP_NOT_FOUND;
  243. handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
  244. GNUNET_SCHEDULER_add_now (&do_error, handle);
  245. return;
  246. }
  247. if (0 >= strlen (name))
  248. {
  249. handle->response_code = MHD_HTTP_NOT_FOUND;
  250. handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
  251. GNUNET_SCHEDULER_add_now (&do_error, handle);
  252. return;
  253. }
  254. handle->name = GNUNET_strdup (name);
  255. handle->record_type = UINT32_MAX;
  256. GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE,
  257. strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE),
  258. &key);
  259. if (GNUNET_YES ==
  260. GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
  261. {
  262. record_type =
  263. GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
  264. handle->record_type = GNUNET_GNSRECORD_typename_to_number (record_type);
  265. }
  266. if (UINT32_MAX == handle->record_type)
  267. {
  268. handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
  269. }
  270. handle->gns_lookup = GNUNET_GNS_lookup_with_tld (handle->gns,
  271. handle->name,
  272. handle->record_type,
  273. GNUNET_GNS_LO_DEFAULT,
  274. &handle_gns_response,
  275. handle);
  276. }
  277. /**
  278. * Respond to OPTIONS request
  279. *
  280. * @param con_handle the connection handle
  281. * @param url the url
  282. * @param cls the RequestHandle
  283. */
  284. static void
  285. options_cont (struct GNUNET_REST_RequestHandle *con_handle,
  286. const char *url,
  287. void *cls)
  288. {
  289. struct MHD_Response *resp;
  290. struct RequestHandle *handle = cls;
  291. // independent of path return all options
  292. resp = GNUNET_REST_create_response (NULL);
  293. MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
  294. handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
  295. GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
  296. return;
  297. }
  298. /**
  299. * Handle rest request
  300. *
  301. * @param handle the request handle
  302. */
  303. static void
  304. init_cont (struct RequestHandle *handle)
  305. {
  306. struct GNUNET_REST_RequestHandlerError err;
  307. static const struct GNUNET_REST_RequestHandler handlers[] =
  308. { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont },
  309. { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont },
  310. GNUNET_REST_HANDLER_END };
  311. if (GNUNET_NO ==
  312. GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
  313. {
  314. handle->response_code = err.error_code;
  315. GNUNET_SCHEDULER_add_now (&do_error, handle);
  316. }
  317. }
  318. /**
  319. * Function processing the REST call
  320. *
  321. * @param method HTTP method
  322. * @param url URL of the HTTP request
  323. * @param data body of the HTTP request (optional)
  324. * @param data_size length of the body
  325. * @param proc callback function for the result
  326. * @param proc_cls closure for callback function
  327. * @return GNUNET_OK if request accepted
  328. */
  329. static void
  330. rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
  331. GNUNET_REST_ResultProcessor proc,
  332. void *proc_cls)
  333. {
  334. struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
  335. handle->response_code = 0;
  336. handle->timeout =
  337. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60);
  338. handle->proc_cls = proc_cls;
  339. handle->proc = proc;
  340. handle->rest_handle = rest_handle;
  341. handle->url = GNUNET_strdup (rest_handle->url);
  342. if (handle->url[strlen (handle->url) - 1] == '/')
  343. handle->url[strlen (handle->url) - 1] = '\0';
  344. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
  345. handle->gns = GNUNET_GNS_connect (cfg);
  346. init_cont (handle);
  347. handle->timeout_task =
  348. GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
  349. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
  350. }
  351. /**
  352. * Entry point for the plugin.
  353. *
  354. * @param cls Config info
  355. * @return NULL on error, otherwise the plugin context
  356. */
  357. void *
  358. libgnunet_plugin_rest_gns_init (void *cls)
  359. {
  360. static struct Plugin plugin;
  361. struct GNUNET_REST_Plugin *api;
  362. cfg = cls;
  363. if (NULL != plugin.cfg)
  364. return NULL; /* can only initialize once! */
  365. memset (&plugin, 0, sizeof(struct Plugin));
  366. plugin.cfg = cfg;
  367. api = GNUNET_new (struct GNUNET_REST_Plugin);
  368. api->cls = &plugin;
  369. api->name = GNUNET_REST_API_NS_GNS;
  370. api->process_request = &rest_process_request;
  371. GNUNET_asprintf (&allow_methods,
  372. "%s, %s, %s, %s, %s",
  373. MHD_HTTP_METHOD_GET,
  374. MHD_HTTP_METHOD_POST,
  375. MHD_HTTP_METHOD_PUT,
  376. MHD_HTTP_METHOD_DELETE,
  377. MHD_HTTP_METHOD_OPTIONS);
  378. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Gns REST API initialized\n"));
  379. return api;
  380. }
  381. /**
  382. * Exit point from the plugin.
  383. *
  384. * @param cls the plugin context (as returned by "init")
  385. * @return always NULL
  386. */
  387. void *
  388. libgnunet_plugin_rest_gns_done (void *cls)
  389. {
  390. struct GNUNET_REST_Plugin *api = cls;
  391. struct Plugin *plugin = api->cls;
  392. plugin->cfg = NULL;
  393. GNUNET_free_non_null (allow_methods);
  394. GNUNET_free (api);
  395. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Gns REST plugin is finished\n");
  396. return NULL;
  397. }
  398. /* end of plugin_rest_gns.c */