plugin_rest_gns.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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. handle->proc (handle->proc_cls, resp, handle->response_code);
  169. json_decref (json_error);
  170. GNUNET_free (response);
  171. GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
  172. }
  173. static void
  174. do_timeout (void *cls)
  175. {
  176. struct RequestHandle *handle = cls;
  177. handle->timeout_task = NULL;
  178. handle->response_code = MHD_HTTP_REQUEST_TIMEOUT;
  179. do_error (handle);
  180. }
  181. /**
  182. * Iterator called on obtained result for a GNS lookup.
  183. *
  184. * @param cls closure with the object
  185. * @param was_gns #GNUNET_NO if name was not a GNS name
  186. * @param rd_count number of records in @a rd
  187. * @param rd the records in reply
  188. */
  189. static void
  190. handle_gns_response (void *cls,
  191. int was_gns,
  192. uint32_t rd_count,
  193. const struct GNUNET_GNSRECORD_Data *rd)
  194. {
  195. struct RequestHandle *handle = cls;
  196. struct MHD_Response *resp;
  197. json_t *result_obj;
  198. char *result;
  199. handle->gns_lookup = NULL;
  200. if (GNUNET_NO == was_gns)
  201. {
  202. handle->response_code = MHD_HTTP_NOT_FOUND;
  203. handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
  204. GNUNET_SCHEDULER_add_now (&do_error, handle);
  205. return;
  206. }
  207. result_obj = GNUNET_JSON_from_gnsrecord (handle->name, rd, rd_count);
  208. result = json_dumps (result_obj, 0);
  209. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
  210. resp = GNUNET_REST_create_response (result);
  211. handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
  212. GNUNET_free (result);
  213. json_decref (result_obj);
  214. GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
  215. }
  216. /**
  217. * Handle gns GET request
  218. *
  219. * @param con_handle the connection handle
  220. * @param url the url
  221. * @param cls the RequestHandle
  222. */
  223. void
  224. get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle,
  225. const char *url,
  226. void *cls)
  227. {
  228. struct RequestHandle *handle = cls;
  229. struct GNUNET_HashCode key;
  230. char *record_type;
  231. char *name;
  232. name = NULL;
  233. handle->name = NULL;
  234. if (strlen (GNUNET_REST_API_NS_GNS) < strlen (handle->url))
  235. {
  236. name = &handle->url[strlen (GNUNET_REST_API_NS_GNS) + 1];
  237. }
  238. if (NULL == name)
  239. {
  240. handle->response_code = MHD_HTTP_NOT_FOUND;
  241. handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
  242. GNUNET_SCHEDULER_add_now (&do_error, handle);
  243. return;
  244. }
  245. if (0 >= strlen (name))
  246. {
  247. handle->response_code = MHD_HTTP_NOT_FOUND;
  248. handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
  249. GNUNET_SCHEDULER_add_now (&do_error, handle);
  250. return;
  251. }
  252. handle->name = GNUNET_strdup (name);
  253. handle->record_type = UINT32_MAX;
  254. GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE,
  255. strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE),
  256. &key);
  257. if (GNUNET_YES ==
  258. GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
  259. {
  260. record_type =
  261. GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
  262. handle->record_type = GNUNET_GNSRECORD_typename_to_number (record_type);
  263. }
  264. if (UINT32_MAX == handle->record_type)
  265. {
  266. handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
  267. }
  268. handle->gns_lookup = GNUNET_GNS_lookup_with_tld (handle->gns,
  269. handle->name,
  270. handle->record_type,
  271. GNUNET_NO,
  272. &handle_gns_response,
  273. handle);
  274. }
  275. /**
  276. * Respond to OPTIONS request
  277. *
  278. * @param con_handle the connection handle
  279. * @param url the url
  280. * @param cls the RequestHandle
  281. */
  282. static void
  283. options_cont (struct GNUNET_REST_RequestHandle *con_handle,
  284. const char *url,
  285. void *cls)
  286. {
  287. struct MHD_Response *resp;
  288. struct RequestHandle *handle = cls;
  289. // independent of path return all options
  290. resp = GNUNET_REST_create_response (NULL);
  291. MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
  292. handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
  293. GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
  294. return;
  295. }
  296. /**
  297. * Handle rest request
  298. *
  299. * @param handle the request handle
  300. */
  301. static void
  302. init_cont (struct RequestHandle *handle)
  303. {
  304. struct GNUNET_REST_RequestHandlerError err;
  305. static const struct GNUNET_REST_RequestHandler handlers[] =
  306. { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont },
  307. { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont },
  308. GNUNET_REST_HANDLER_END };
  309. if (GNUNET_NO ==
  310. GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
  311. {
  312. handle->response_code = err.error_code;
  313. GNUNET_SCHEDULER_add_now (&do_error, handle);
  314. }
  315. }
  316. /**
  317. * Function processing the REST call
  318. *
  319. * @param method HTTP method
  320. * @param url URL of the HTTP request
  321. * @param data body of the HTTP request (optional)
  322. * @param data_size length of the body
  323. * @param proc callback function for the result
  324. * @param proc_cls closure for callback function
  325. * @return GNUNET_OK if request accepted
  326. */
  327. static void
  328. rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
  329. GNUNET_REST_ResultProcessor proc,
  330. void *proc_cls)
  331. {
  332. struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
  333. handle->response_code = 0;
  334. handle->timeout =
  335. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60);
  336. handle->proc_cls = proc_cls;
  337. handle->proc = proc;
  338. handle->rest_handle = rest_handle;
  339. handle->url = GNUNET_strdup (rest_handle->url);
  340. if (handle->url[strlen (handle->url) - 1] == '/')
  341. handle->url[strlen (handle->url) - 1] = '\0';
  342. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
  343. handle->gns = GNUNET_GNS_connect (cfg);
  344. init_cont (handle);
  345. handle->timeout_task =
  346. GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
  347. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
  348. }
  349. /**
  350. * Entry point for the plugin.
  351. *
  352. * @param cls Config info
  353. * @return NULL on error, otherwise the plugin context
  354. */
  355. void *
  356. libgnunet_plugin_rest_gns_init (void *cls)
  357. {
  358. static struct Plugin plugin;
  359. struct GNUNET_REST_Plugin *api;
  360. cfg = cls;
  361. if (NULL != plugin.cfg)
  362. return NULL; /* can only initialize once! */
  363. memset (&plugin, 0, sizeof(struct Plugin));
  364. plugin.cfg = cfg;
  365. api = GNUNET_new (struct GNUNET_REST_Plugin);
  366. api->cls = &plugin;
  367. api->name = GNUNET_REST_API_NS_GNS;
  368. api->process_request = &rest_process_request;
  369. GNUNET_asprintf (&allow_methods,
  370. "%s, %s, %s, %s, %s",
  371. MHD_HTTP_METHOD_GET,
  372. MHD_HTTP_METHOD_POST,
  373. MHD_HTTP_METHOD_PUT,
  374. MHD_HTTP_METHOD_DELETE,
  375. MHD_HTTP_METHOD_OPTIONS);
  376. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Gns REST API initialized\n"));
  377. return api;
  378. }
  379. /**
  380. * Exit point from the plugin.
  381. *
  382. * @param cls the plugin context (as returned by "init")
  383. * @return always NULL
  384. */
  385. void *
  386. libgnunet_plugin_rest_gns_done (void *cls)
  387. {
  388. struct GNUNET_REST_Plugin *api = cls;
  389. struct Plugin *plugin = api->cls;
  390. plugin->cfg = NULL;
  391. GNUNET_free_non_null (allow_methods);
  392. GNUNET_free (api);
  393. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Gns REST plugin is finished\n");
  394. return NULL;
  395. }
  396. /* end of plugin_rest_gns.c */