2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 GNUnet e.V.
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.
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.
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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 * @author Martin Schanzenbach
22 * @file gns/plugin_rest_gns.c
23 * @brief GNUnet GNS REST plugin
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 <gnunet_rest_lib.h>
35 #include <gnunet_jsonapi_lib.h>
38 #define GNUNET_REST_API_NS_GNS "/gns"
40 #define GNUNET_REST_JSONAPI_GNS_RECORD_TYPE "record_type"
42 #define GNUNET_REST_JSONAPI_GNS_TYPEINFO "gns_name"
44 #define GNUNET_REST_JSONAPI_GNS_RECORD "records"
46 #define GNUNET_REST_JSONAPI_GNS_EGO "ego"
48 #define GNUNET_REST_JSONAPI_GNS_PKEY "pkey"
50 #define GNUNET_REST_JSONAPI_GNS_OPTIONS "options"
53 * @brief struct returned by the initialization function of the plugin
57 const struct GNUNET_CONFIGURATION_Handle *cfg;
60 const struct GNUNET_CONFIGURATION_Handle *cfg;
65 * Handle to GNS service.
67 struct GNUNET_GNS_Handle *gns;
70 * Desired timeout for the lookup (default is no timeout).
72 struct GNUNET_TIME_Relative timeout;
75 * Handle to lookup request
77 struct GNUNET_GNS_LookupRequest *lookup_request;
80 * Handle to rest request
82 struct GNUNET_REST_RequestHandle *rest_handle;
85 * Lookup an ego with the identity service.
87 struct GNUNET_IDENTITY_EgoLookup *el;
90 * Handle for identity service.
92 struct GNUNET_IDENTITY_Handle *identity;
95 * Active operation on identity service.
97 struct GNUNET_IDENTITY_Operation *id_op;
100 * ID of a task associated with the resolution process.
102 struct GNUNET_SCHEDULER_Task * timeout_task;
105 * The root of the received JSON or NULL
110 * The plugin result processor
112 GNUNET_REST_ResultProcessor proc;
115 * The closure of the result processor
120 * The name to look up
126 * In string representation from JSON
132 * In string representation from JSON
134 const char *pkey_str;
142 * The public key of to use for lookup
144 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
147 * The public key to use for lookup
149 struct GNUNET_CRYPTO_EcdsaPublicKey pkeym;
152 * The resolver options
154 enum GNUNET_GNS_LocalOptions options;
159 struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_key;
170 * Cleanup lookup handle.
172 * @param handle Handle to clean up
175 cleanup_handle (struct LookupHandle *handle)
177 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
179 if (NULL != handle->json_root)
180 json_decref (handle->json_root);
182 if (NULL != handle->name)
183 GNUNET_free (handle->name);
184 if (NULL != handle->el)
186 GNUNET_IDENTITY_ego_lookup_cancel (handle->el);
189 if (NULL != handle->id_op)
191 GNUNET_IDENTITY_cancel (handle->id_op);
192 handle->id_op = NULL;
194 if (NULL != handle->lookup_request)
196 GNUNET_GNS_lookup_cancel (handle->lookup_request);
197 handle->lookup_request = NULL;
199 if (NULL != handle->identity)
201 GNUNET_IDENTITY_disconnect (handle->identity);
202 handle->identity = NULL;
204 if (NULL != handle->gns)
206 GNUNET_GNS_disconnect (handle->gns);
210 if (NULL != handle->timeout_task)
212 GNUNET_SCHEDULER_cancel (handle->timeout_task);
214 GNUNET_free (handle);
219 * Task run on shutdown. Cleans up everything.
222 * @param tc scheduler context
227 struct LookupHandle *handle = cls;
228 struct MHD_Response *resp;
230 resp = GNUNET_REST_create_json_response (NULL);
231 handle->proc (handle->proc_cls, resp, handle->response_code);
232 cleanup_handle (handle);
237 * Create json representation of a GNSRECORD
239 * @param rd the GNSRECORD_Data
242 gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd)
244 const char *typename;
249 typename = GNUNET_GNSRECORD_number_to_typename (rd->record_type);
250 string_val = GNUNET_GNSRECORD_value_to_string (rd->record_type,
254 if (NULL == string_val)
256 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
257 "Record of type %d malformed, skipping\n",
258 (int) rd->record_type);
261 record_obj = json_object ();
262 json_object_set_new (record_obj, "type", json_string (typename));
263 json_object_set_new (record_obj, "value", json_string (string_val));
264 GNUNET_free (string_val);
266 if (GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION & rd->flags)
268 struct GNUNET_TIME_Relative time_rel;
269 time_rel.rel_value_us = rd->expiration_time;
270 exp_str = GNUNET_STRINGS_relative_time_to_string (time_rel, 1);
274 struct GNUNET_TIME_Absolute time_abs;
275 time_abs.abs_value_us = rd->expiration_time;
276 exp_str = GNUNET_STRINGS_absolute_time_to_string (time_abs);
278 json_object_set_new (record_obj, "expiration_time", json_string (exp_str));
280 json_object_set_new (record_obj, "expired",
281 json_boolean (GNUNET_YES == GNUNET_GNSRECORD_is_expired (rd)));
286 * Function called with the result of a GNS lookup.
288 * @param cls the 'const char *' name that was resolved
289 * @param rd_count number of records returned
290 * @param rd array of @a rd_count records with the results
293 process_lookup_result (void *cls, uint32_t rd_count,
294 const struct GNUNET_GNSRECORD_Data *rd)
296 struct LookupHandle *handle = cls;
297 struct MHD_Response *resp;
298 struct GNUNET_JSONAPI_Document *json_document;
299 struct GNUNET_JSONAPI_Resource *json_resource;
302 json_t *result_array;
305 result_array = json_array();
306 json_document = GNUNET_JSONAPI_document_new ();
307 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_GNS_TYPEINFO, handle->name);
308 handle->lookup_request = NULL;
309 for (i=0; i<rd_count; i++)
311 if ( (rd[i].record_type != handle->type) &&
312 (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
314 record_obj = gnsrecord_to_json (&(rd[i]));
315 json_array_append (result_array, record_obj);
316 json_decref (record_obj);
318 GNUNET_JSONAPI_resource_add_attr (json_resource,
319 GNUNET_REST_JSONAPI_GNS_RECORD,
321 GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
322 GNUNET_JSONAPI_document_serialize (json_document, &result);
323 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
324 json_decref (result_array);
325 GNUNET_JSONAPI_document_delete (json_document);
326 resp = GNUNET_REST_create_json_response (result);
327 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
328 GNUNET_free (result);
329 cleanup_handle (handle);
334 * Perform the actual resolution, starting with the zone
335 * identified by the given public key and the shorten zone.
337 * @param pkey public key to use for the zone, can be NULL
338 * @param shorten_key private key used for shortening, can be NULL
341 lookup_with_keys (struct LookupHandle *handle, const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key)
343 if (UINT32_MAX == handle->type)
345 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
346 _("Invalid typename specified, assuming `ANY'\n"));
347 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
349 if (NULL != handle->name)
351 handle->lookup_request = GNUNET_GNS_lookup (handle->gns,
357 &process_lookup_result,
362 GNUNET_SCHEDULER_add_now (&do_error, handle);
368 * Method called to with the ego we are to use for shortening
371 * @param cls closure contains the public key to use
372 * @param ego ego handle, NULL if not found
373 * @param ctx context for application to store data for this ego
374 * (during the lifetime of this process, initially NULL)
375 * @param name name assigned by the user for this ego,
376 * NULL if the user just deleted the ego and it
377 * must thus no longer be used
380 identity_shorten_cb (void *cls,
381 struct GNUNET_IDENTITY_Ego *ego,
385 struct LookupHandle *handle = cls;
387 handle->id_op = NULL;
389 lookup_with_keys (handle, NULL);
391 lookup_with_keys (handle,
392 GNUNET_IDENTITY_ego_get_private_key (ego));
396 * Perform the actual resolution, starting with the zone
397 * identified by the given public key.
399 * @param pkey public key to use for the zone
402 lookup_with_public_key (struct LookupHandle *handle)
404 handle->pkeym = handle->pkey;
405 GNUNET_break (NULL == handle->id_op);
406 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
408 &identity_shorten_cb,
410 if (NULL == handle->id_op)
413 lookup_with_keys (handle, NULL);
418 * Method called to with the ego we are to use for the lookup,
419 * when the ego is determined by a name.
421 * @param cls closure (NULL, unused)
422 * @param ego ego handle, NULL if not found
425 identity_zone_cb (void *cls,
426 const struct GNUNET_IDENTITY_Ego *ego)
428 struct LookupHandle *handle = cls;
433 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
434 _("Ego for not found, cannot perform lookup.\n"));
435 GNUNET_SCHEDULER_add_now (&do_error, handle);
440 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
441 lookup_with_public_key (handle);
443 json_decref(handle->json_root);
447 * Method called to with the ego we are to use for the lookup,
448 * when the ego is the one for the default master zone.
450 * @param cls closure (NULL, unused)
451 * @param ego ego handle, NULL if not found
452 * @param ctx context for application to store data for this ego
453 * (during the lifetime of this process, initially NULL)
454 * @param name name assigned by the user for this ego,
455 * NULL if the user just deleted the ego and it
456 * must thus no longer be used
459 identity_master_cb (void *cls,
460 struct GNUNET_IDENTITY_Ego *ego,
465 struct LookupHandle *handle = cls;
467 handle->id_op = NULL;
470 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
471 _("Ego for `gns-master' not found, cannot perform lookup. Did you run gnunet-gns-import.sh?\n"));
472 GNUNET_SCHEDULER_add_now (&do_error, handle);
475 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
476 /* main name is our own master zone, do no look for that in the DHT */
477 handle->options = GNUNET_GNS_LO_LOCAL_MASTER;
478 /* if the name is of the form 'label.gnu', never go to the DHT */
480 if (NULL != handle->name)
481 dot = strchr (handle->name, '.');
482 if ( (NULL != dot) &&
483 (0 == strcasecmp (dot, ".gnu")) )
484 handle->options = GNUNET_GNS_LO_NO_DHT;
485 lookup_with_public_key (handle);
489 * Parse REST uri for name and record type
491 * @param url Url to parse
492 * @param handle lookup handle to populate
493 * @return GNUNET_SYSERR on error
496 parse_url (const char *url, struct LookupHandle *handle)
499 char tmp_url[strlen(url)+1];
502 strcpy (tmp_url, url);
503 tok = strtok ((char*)tmp_url, "/");
505 return GNUNET_SYSERR;
506 name = strtok (NULL, "/");
508 return GNUNET_SYSERR;
509 GNUNET_asprintf (&handle->name,
512 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
513 "Got name: %s\n", handle->name);
518 get_gns_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
522 struct LookupHandle *handle = cls;
523 struct GNUNET_HashCode key;
525 //parse name and type from url
526 if (GNUNET_OK != parse_url (url, handle))
528 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing url...\n");
529 GNUNET_SCHEDULER_add_now (&do_error, handle);
532 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
534 handle->gns = GNUNET_GNS_connect (cfg);
535 handle->identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL);
536 handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
538 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
540 if (NULL == handle->gns)
542 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
543 "Connecting to GNS failed\n");
544 GNUNET_SCHEDULER_add_now (&do_error, handle);
547 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_OPTIONS,
548 strlen (GNUNET_REST_JSONAPI_GNS_OPTIONS),
550 handle->options = GNUNET_GNS_LO_DEFAULT;
552 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
555 handle->options = GNUNET_GNS_LO_DEFAULT;//TODO(char*) GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
558 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE,
559 strlen (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE),
562 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
565 handle->type = GNUNET_GNSRECORD_typename_to_number
566 (GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
570 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
572 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_PKEY,
573 strlen (GNUNET_REST_JSONAPI_GNS_PKEY),
576 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
579 handle->pkey_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
582 GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->pkey_str,
583 strlen(handle->pkey_str),
586 GNUNET_SCHEDULER_add_now (&do_error, handle);
589 lookup_with_public_key (handle);
592 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_EGO,
593 strlen (GNUNET_REST_JSONAPI_GNS_EGO),
596 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
599 handle->ego_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
601 handle->el = GNUNET_IDENTITY_ego_lookup (cfg,
607 if ( (NULL != handle->name) &&
608 (strlen (handle->name) > 4) &&
609 (0 == strcmp (".zkey",
610 &handle->name[strlen (handle->name) - 4])) )
612 GNUNET_CRYPTO_ecdsa_key_get_public
613 (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
615 lookup_with_public_key (handle);
619 GNUNET_break (NULL == handle->id_op);
620 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
624 GNUNET_assert (NULL != handle->id_op);
629 * Handle rest request
631 * @param handle the lookup handle
634 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
638 struct MHD_Response *resp;
639 struct LookupHandle *handle = cls;
641 //For GNS, independent of path return all options
642 resp = GNUNET_REST_create_json_response (NULL);
643 MHD_add_response_header (resp,
644 "Access-Control-Allow-Methods",
645 MHD_HTTP_METHOD_GET);
646 handle->proc (handle->proc_cls,
649 cleanup_handle (handle);
654 * Function processing the REST call
656 * @param method HTTP method
657 * @param url URL of the HTTP request
658 * @param data body of the HTTP request (optional)
659 * @param data_size length of the body
660 * @param proc callback function for the result
661 * @param proc_cls closure for callback function
662 * @return GNUNET_OK if request accepted
665 rest_gns_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
666 GNUNET_REST_ResultProcessor proc,
669 struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
670 struct GNUNET_REST_RequestHandlerError err;
672 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
673 handle->proc_cls = proc_cls;
675 handle->rest_handle = conndata_handle;
677 static const struct GNUNET_REST_RequestHandler handlers[] = {
678 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont},
679 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont},
680 GNUNET_REST_HANDLER_END
683 if (GNUNET_NO == GNUNET_JSONAPI_handle_request (conndata_handle,
688 handle->response_code = err.error_code;
689 GNUNET_SCHEDULER_add_now (&do_error, handle);
695 * Entry point for the plugin.
697 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
698 * @return NULL on error, otherwise the plugin context
701 libgnunet_plugin_rest_gns_init (void *cls)
703 static struct Plugin plugin;
705 struct GNUNET_REST_Plugin *api;
707 if (NULL != plugin.cfg)
708 return NULL; /* can only initialize once! */
709 memset (&plugin, 0, sizeof (struct Plugin));
711 api = GNUNET_new (struct GNUNET_REST_Plugin);
713 api->name = GNUNET_REST_API_NS_GNS;
714 api->process_request = &rest_gns_process_request;
715 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
716 _("GNS REST API initialized\n"));
722 * Exit point from the plugin.
724 * @param cls the plugin context (as returned by "init")
725 * @return always NULL
728 libgnunet_plugin_rest_gns_done (void *cls)
730 struct GNUNET_REST_Plugin *api = cls;
731 struct Plugin *plugin = api->cls;
735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736 "GNS REST plugin is finished\n");
740 /* end of plugin_rest_gns.c */