aboutsummaryrefslogtreecommitdiff
path: root/src/gns/plugin_rest_gns.c
diff options
context:
space:
mode:
authorMartin Schanzenbach <mschanzenbach@posteo.de>2015-03-11 12:54:22 +0000
committerMartin Schanzenbach <mschanzenbach@posteo.de>2015-03-11 12:54:22 +0000
commit86882d3e761fe639e833446ccfef8d31e0e42998 (patch)
tree0d7c054aed321dff8b3a0f469068e546bdfc7f2b /src/gns/plugin_rest_gns.c
parent4bbe44b460db015f8c784d4b699f6d381d2d96c3 (diff)
downloadgnunet-86882d3e761fe639e833446ccfef8d31e0e42998.tar.gz
gnunet-86882d3e761fe639e833446ccfef8d31e0e42998.zip
REST Service
Diffstat (limited to 'src/gns/plugin_rest_gns.c')
-rw-r--r--src/gns/plugin_rest_gns.c658
1 files changed, 658 insertions, 0 deletions
diff --git a/src/gns/plugin_rest_gns.c b/src/gns/plugin_rest_gns.c
new file mode 100644
index 000000000..bd28c23f3
--- /dev/null
+++ b/src/gns/plugin_rest_gns.c
@@ -0,0 +1,658 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @author Martin Schanzenbach
22 * @file gns/plugin_rest_gns.c
23 * @brief GNUnet GNS REST plugin
24 *
25 */
26
27#include "platform.h"
28#include "gnunet_rest_plugin.h"
29#include <gnunet_dnsparser_lib.h>
30#include <gnunet_identity_service.h>
31#include <gnunet_gnsrecord_lib.h>
32#include <gnunet_namestore_service.h>
33#include <gnunet_gns_service.h>
34#include <jansson.h>
35
36#define API_NAMESPACE "gns"
37
38/**
39 * @brief struct returned by the initialization function of the plugin
40 */
41struct Plugin
42{
43 const struct GNUNET_CONFIGURATION_Handle *cfg;
44};
45
46const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48struct LookupHandle
49{
50 /**
51 * Handle to GNS service.
52 */
53 struct GNUNET_GNS_Handle *gns;
54
55 /**
56 * Desired timeout for the lookup (default is no timeout).
57 */
58 struct GNUNET_TIME_Relative timeout;
59
60 /**
61 * Handle to lookup request
62 */
63 struct GNUNET_GNS_LookupRequest *lookup_request;
64
65 /**
66 * Lookup an ego with the identity service.
67 */
68 struct GNUNET_IDENTITY_EgoLookup *el;
69
70 /**
71 * Handle for identity service.
72 */
73 struct GNUNET_IDENTITY_Handle *identity;
74
75 /**
76 * Active operation on identity service.
77 */
78 struct GNUNET_IDENTITY_Operation *id_op;
79 /**
80 * ID of a task associated with the resolution process.
81 */
82 struct GNUNET_SCHEDULER_Task * timeout_task;
83
84 json_t *json_root;
85
86 GNUNET_REST_ResultProcessor proc;
87
88 char *name;
89
90 const char *ego_str;
91
92 const char *pkey_str;
93
94 int type;
95
96 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
97
98 struct GNUNET_CRYPTO_EcdsaPublicKey pkeym;
99
100 enum GNUNET_GNS_LocalOptions options;
101
102 struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_key;
103 void *proc_cls;
104};
105
106void
107cleanup_handle (struct LookupHandle *handle)
108{
109 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
110 "Cleaning up\n");
111 if (NULL != handle->json_root)
112 json_decref (handle->json_root);
113
114 if (NULL != handle->name)
115 GNUNET_free (handle->name);
116 if (NULL != handle->el)
117 {
118 GNUNET_IDENTITY_ego_lookup_cancel (handle->el);
119 handle->el = NULL;
120 }
121 if (NULL != handle->id_op)
122 {
123 GNUNET_IDENTITY_cancel (handle->id_op);
124 handle->id_op = NULL;
125 }
126 if (NULL != handle->lookup_request)
127 {
128 GNUNET_GNS_lookup_cancel (handle->lookup_request);
129 handle->lookup_request = NULL;
130 }
131 if (NULL != handle->identity)
132 {
133 GNUNET_IDENTITY_disconnect (handle->identity);
134 handle->identity = NULL;
135 }
136 if (NULL != handle->gns)
137 {
138 GNUNET_GNS_disconnect (handle->gns);
139 handle->gns = NULL;
140 }
141
142 if (NULL != handle->timeout_task)
143 {
144 GNUNET_SCHEDULER_cancel (handle->timeout_task);
145 }
146 GNUNET_free (handle);
147}
148
149
150/**
151 * Task run on shutdown. Cleans up everything.
152 *
153 * @param cls unused
154 * @param tc scheduler context
155 */
156static void
157do_error (void *cls,
158 const struct GNUNET_SCHEDULER_TaskContext *tc)
159{
160 struct LookupHandle *handle = cls;
161
162 cleanup_handle (handle);
163 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
164}
165
166/**
167 * Function called with the result of a GNS lookup.
168 *
169 * @param cls the 'const char *' name that was resolved
170 * @param rd_count number of records returned
171 * @param rd array of @a rd_count records with the results
172 */
173static void
174process_lookup_result (void *cls, uint32_t rd_count,
175 const struct GNUNET_GNSRECORD_Data *rd)
176{
177 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
178 "result \n");
179 struct LookupHandle *handle = cls;
180 uint32_t i;
181 const char *typename;
182 char *string_val;
183 char *result;
184 json_t *result_root;
185 json_t *result_name;
186 json_t *result_array;
187 json_t *record_obj;
188
189
190 result_root = json_object();
191 result_name = json_string (handle->name);
192 result_array = json_array();
193 json_object_set (result_root, "name", result_name);
194 json_decref (result_name);
195
196 handle->lookup_request = NULL;
197
198 for (i=0; i<rd_count; i++)
199 {
200 if ( (rd[i].record_type != handle->type) &&
201 (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
202 continue;
203 typename = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
204 string_val = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
205 rd[i].data,
206 rd[i].data_size);
207 if (NULL == string_val)
208 {
209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
210 "Record %u of type %d malformed, skipping\n",
211 (unsigned int) i,
212 (int) rd[i].record_type);
213 continue;
214 }
215 else
216 {
217 record_obj = json_object();
218 json_object_set_new (record_obj, "type", json_string (typename));
219 json_object_set_new (record_obj, "value", json_string (string_val));
220 json_array_append (result_array, record_obj);
221 json_decref (record_obj);
222 }
223 GNUNET_free (string_val);
224 }
225 json_object_set (result_root, "query_result", result_array);
226 json_decref (result_array);
227 result = json_dumps (result_root, JSON_COMPACT);
228 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Result %s\n", result);
229 json_decref (result_root);
230 handle->proc (handle->proc_cls, result, strlen (result), GNUNET_OK);
231 GNUNET_free (result);
232 cleanup_handle (handle);
233}
234
235/**
236 * Perform the actual resolution, starting with the zone
237 * identified by the given public key and the shorten zone.
238 *
239 * @param pkey public key to use for the zone, can be NULL
240 * @param shorten_key private key used for shortening, can be NULL
241 */
242static void
243lookup_with_keys (struct LookupHandle *handle, const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key)
244{
245 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
246 "Lookup w/ keys \n");
247 if (UINT32_MAX == handle->type)
248 {
249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250 _("Invalid typename specified, assuming `ANY'\n"));
251 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
252 }
253
254 if (NULL != handle->name)
255 {
256 handle->lookup_request = GNUNET_GNS_lookup (handle->gns,
257 handle->name,
258 &handle->pkey,
259 handle->type,
260 handle->options,
261 shorten_key,
262 &process_lookup_result,
263 handle);
264 }
265 else
266 {
267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
268 _("Please specify name to lookup!\n"));
269 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
270 cleanup_handle (handle);
271 return;
272 }
273}
274
275/**
276 * Method called to with the ego we are to use for shortening
277 * during the lookup.
278 *
279 * @param cls closure contains the public key to use
280 * @param ego ego handle, NULL if not found
281 * @param ctx context for application to store data for this ego
282 * (during the lifetime of this process, initially NULL)
283 * @param name name assigned by the user for this ego,
284 * NULL if the user just deleted the ego and it
285 * must thus no longer be used
286 */
287static void
288identity_shorten_cb (void *cls,
289 struct GNUNET_IDENTITY_Ego *ego,
290 void **ctx,
291 const char *name)
292{
293 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
294 "shorten \n");
295 struct LookupHandle *handle = cls;
296
297 handle->id_op = NULL;
298 if (NULL == ego)
299 lookup_with_keys (handle, NULL);
300 else
301 lookup_with_keys (handle,
302 GNUNET_IDENTITY_ego_get_private_key (ego));
303}
304
305/**
306 * Perform the actual resolution, starting with the zone
307 * identified by the given public key.
308 *
309 * @param pkey public key to use for the zone
310 */
311static void
312lookup_with_public_key (struct LookupHandle *handle)
313{
314 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
315 "Lookup w/ pkey \n");
316 handle->pkeym = handle->pkey;
317 GNUNET_break (NULL == handle->id_op);
318 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
319 "gns-short",
320 &identity_shorten_cb,
321 handle);
322 if (NULL == handle->id_op)
323 {
324 GNUNET_break (0);
325 lookup_with_keys (handle, NULL);
326 }
327}
328
329/**
330 * Method called to with the ego we are to use for the lookup,
331 * when the ego is determined by a name.
332 *
333 * @param cls closure (NULL, unused)
334 * @param ego ego handle, NULL if not found
335 */
336static void
337identity_zone_cb (void *cls,
338 const struct GNUNET_IDENTITY_Ego *ego)
339{
340 struct LookupHandle *handle = cls;
341
342 handle->el = NULL;
343 if (NULL == ego)
344 {
345 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
346 _("Ego for not found, cannot perform lookup.\n"));
347 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
348 cleanup_handle (handle);
349 return;
350 }
351 else
352 {
353 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
354 lookup_with_public_key (handle);
355 }
356 json_decref(handle->json_root);
357}
358
359/**
360 * Method called to with the ego we are to use for the lookup,
361 * when the ego is the one for the default master zone.
362 *
363 * @param cls closure (NULL, unused)
364 * @param ego ego handle, NULL if not found
365 * @param ctx context for application to store data for this ego
366 * (during the lifetime of this process, initially NULL)
367 * @param name name assigned by the user for this ego,
368 * NULL if the user just deleted the ego and it
369 * must thus no longer be used
370 */
371static void
372identity_master_cb (void *cls,
373 struct GNUNET_IDENTITY_Ego *ego,
374 void **ctx,
375 const char *name)
376{
377 const char *dot;
378 struct LookupHandle *handle = cls;
379
380 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
381 "Master cb \n");
382 handle->id_op = NULL;
383 if (NULL == ego)
384 {
385 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
386 _("Ego for `gns-master' not found, cannot perform lookup. Did you run gnunet-gns-import.sh?\n"));
387 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
388 cleanup_handle (handle);
389 return;
390 }
391 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
392 /* main name is our own master zone, do no look for that in the DHT */
393 handle->options = GNUNET_GNS_LO_LOCAL_MASTER;
394 /* if the name is of the form 'label.gnu', never go to the DHT */
395 dot = NULL;
396 if (NULL != handle->name)
397 dot = strchr (handle->name, '.');
398 if ( (NULL != dot) &&
399 (0 == strcasecmp (dot, ".gnu")) )
400 handle->options = GNUNET_GNS_LO_NO_DHT;
401 lookup_with_public_key (handle);
402}
403
404int
405parse_url (const char *url, struct LookupHandle *handle)
406{
407 char *name;
408 char *type;
409 char tmp_url[strlen(url)+1];
410 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Parsing %s\n", url);
411 strcpy (tmp_url, url);
412
413 char *tok = strtok ((char*)tmp_url, "/");
414
415
416 if (NULL == tok)
417 return GNUNET_SYSERR;
418
419 name = strtok (NULL, "/");
420
421 if (NULL == name)
422 return GNUNET_SYSERR;
423
424 GNUNET_asprintf (&handle->name,
425 "%s",
426 name);
427
428 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429 "Got name: %s\n", handle->name);
430
431 type = strtok (NULL, "/");
432
433 if (NULL == type)
434 {
435 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
436 return GNUNET_OK;
437 }
438
439 handle->type = GNUNET_GNSRECORD_typename_to_number (type);
440
441 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
442 "Got type: %s\n", type);
443 return GNUNET_OK;
444}
445
446int
447parse_json (const char *data, size_t data_size, struct LookupHandle *handle)
448{
449 json_error_t error;
450 json_t *pkey_json;
451 json_t *ego_json;
452 json_t *options_json;
453
454 char term_data[data_size+1];
455 term_data[data_size] = '\0';
456
457 memcpy (term_data, data, data_size);
458
459 handle->json_root = json_loads (term_data, 0, &error);
460
461 if (NULL == handle->json_root)
462 {
463 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, error.text);
464 return GNUNET_SYSERR;
465 }
466
467 if(!json_is_object(handle->json_root))
468 {
469 return GNUNET_SYSERR;
470 }
471
472 ego_json = json_object_get (handle->json_root, "ego");
473
474 if(json_is_string(ego_json))
475 {
476 handle->ego_str = json_string_value (ego_json);
477 }
478
479 pkey_json = json_object_get (handle->json_root, "pkey");
480 if(json_is_string(pkey_json))
481 {
482 handle->pkey_str = json_string_value (pkey_json);
483 }
484
485 options_json = json_object_get (handle->json_root, "options");
486 if(json_is_integer (options_json))
487 {
488 handle->options = json_integer_value (options_json);
489 }
490 return GNUNET_OK;
491}
492
493
494/**
495 * Function processing the REST call
496 *
497 * @param method HTTP method
498 * @param url URL of the HTTP request
499 * @param data body of the HTTP request (optional)
500 * @param data_size length of the body
501 * @param proc callback function for the result
502 * @param proc_cls closure for callback function
503 * @return GNUNET_OK if request accepted
504 */
505void
506rest_gns_process_request(const char *method,
507 const char *url,
508 const char *data,
509 size_t data_size,
510 GNUNET_REST_ResultProcessor proc,
511 void *proc_cls)
512{
513 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
514 "In plugin\n");
515 struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
516
517 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; //TODO read from json
518
519 //parse name and type from url
520 if (GNUNET_OK != parse_url (url, handle))
521 {
522 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing url...\n");
523 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
524 cleanup_handle (handle);
525 return;
526 }
527
528 handle->proc_cls = proc_cls;
529 handle->proc = proc;
530
531 if (0 < data_size)
532 {
533 if (GNUNET_OK != parse_json (data, data_size, handle))
534 {
535 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing json...\n");
536 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
537 cleanup_handle (handle);
538 return;
539 }
540 }
541 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
542 "Connecting...\n");
543 handle->gns = GNUNET_GNS_connect (cfg);
544 handle->identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL);
545
546 handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
547 &do_error, handle);
548 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
549 "Connected\n");
550
551 if (NULL == handle->gns)
552 {
553 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
554 "Connecting to GNS failed\n");
555 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
556 cleanup_handle (handle);
557 return;
558 }
559
560 if (NULL != handle->pkey_str)
561 {
562 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
563 "pkey_str\n");
564 if (GNUNET_OK !=
565 GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->pkey_str,
566 strlen(handle->pkey_str),
567 &(handle->pkey)))
568 {
569 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
570 cleanup_handle (handle);
571 return;
572 }
573 lookup_with_public_key (handle);
574
575 }
576 if (NULL != handle->ego_str)
577 {
578 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
579 "ego_str\n");
580 handle->el = GNUNET_IDENTITY_ego_lookup (cfg,
581 handle->ego_str,
582 &identity_zone_cb,
583 handle);
584 return;
585 }
586 if ( (NULL != handle->name) &&
587 (strlen (handle->name) > 4) &&
588 (0 == strcmp (".zkey",
589 &handle->name[strlen (handle->name) - 4])) )
590 {
591 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
592 "zkey\n");
593 /* no zone required, use 'anonymous' zone */
594 GNUNET_CRYPTO_ecdsa_key_get_public
595 (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
596 &(handle->pkey));
597 lookup_with_public_key (handle);
598 }
599 else
600 {
601 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
602 "gns_master\n");
603 GNUNET_break (NULL == handle->id_op);
604 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
605 "gns-master",
606 &identity_master_cb,
607 handle);
608 GNUNET_assert (NULL != handle->id_op);
609 }
610}
611
612/**
613 * Entry point for the plugin.
614 *
615 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
616 * @return NULL on error, otherwise the plugin context
617 */
618void *
619libgnunet_plugin_rest_gns_init (void *cls)
620{
621 static struct Plugin plugin;
622 cfg = cls;
623 struct GNUNET_REST_Plugin *api;
624
625 if (NULL != plugin.cfg)
626 return NULL; /* can only initialize once! */
627 memset (&plugin, 0, sizeof (struct Plugin));
628 plugin.cfg = cfg;
629 api = GNUNET_new (struct GNUNET_REST_Plugin);
630 api->cls = &plugin;
631 api->name = API_NAMESPACE;
632 api->process_request = &rest_gns_process_request;
633 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
634 _("GNS REST API initialized\n"));
635 return api;
636}
637
638
639/**
640 * Exit point from the plugin.
641 *
642 * @param cls the plugin context (as returned by "init")
643 * @return always NULL
644 */
645void *
646libgnunet_plugin_namestore_sqlite_done (void *cls)
647{
648 struct GNUNET_REST_Plugin *api = cls;
649 struct Plugin *plugin = api->cls;
650
651 plugin->cfg = NULL;
652 GNUNET_free (api);
653 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
654 "GNS REST plugin is finished\n");
655 return NULL;
656}
657
658/* end of plugin_rest_gns.c */