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