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 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.
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.
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/>.
19 * @author Martin Schanzenbach
20 * @author Philippe Buschmann
21 * @file gns/plugin_rest_gns.c
22 * @brief GNUnet GNS REST plugin
27 #include "gnunet_rest_plugin.h"
28 #include <gnunet_dnsparser_lib.h>
29 #include <gnunet_identity_service.h>
30 #include <gnunet_gnsrecord_lib.h>
31 #include <gnunet_namestore_service.h>
32 #include <gnunet_gns_service.h>
33 #include <gnunet_rest_lib.h>
34 #include <gnunet_jsonapi_lib.h>
35 #include <gnunet_jsonapi_util.h>
38 #define GNUNET_REST_API_NS_GNS "/gns"
40 #define GNUNET_REST_JSONAPI_GNS_RECORD_TYPE "record_type"
42 #define GNUNET_REST_PARAMETER_GNS_NAME "name"
44 #define GNUNET_REST_JSONAPI_GNS_TYPEINFO "gns_name"
46 #define GNUNET_REST_JSONAPI_GNS_RECORD "records"
48 #define GNUNET_REST_JSONAPI_GNS_EGO "ego"
50 #define GNUNET_REST_JSONAPI_GNS_PKEY "pkey"
52 #define GNUNET_REST_JSONAPI_GNS_OPTIONS "options"
55 * @brief struct returned by the initialization function of the plugin
59 const struct GNUNET_CONFIGURATION_Handle *cfg;
62 const struct GNUNET_CONFIGURATION_Handle *cfg;
67 * Handle to GNS service.
69 struct GNUNET_GNS_Handle *gns;
72 * Desired timeout for the lookup (default is no timeout).
74 struct GNUNET_TIME_Relative timeout;
77 * Handle to lookup request
79 struct GNUNET_GNS_LookupRequest *lookup_request;
82 * Handle to rest request
84 struct GNUNET_REST_RequestHandle *rest_handle;
87 * Lookup an ego with the identity service.
89 struct GNUNET_IDENTITY_EgoLookup *el;
92 * Handle for identity service.
94 struct GNUNET_IDENTITY_Handle *identity;
97 * Active operation on identity service.
99 struct GNUNET_IDENTITY_Operation *id_op;
102 * ID of a task associated with the resolution process.
104 struct GNUNET_SCHEDULER_Task * timeout_task;
107 * The root of the received JSON or NULL
112 * The plugin result processor
114 GNUNET_REST_ResultProcessor proc;
117 * The closure of the result processor
122 * The name to look up
128 * In string representation from JSON
134 * In string representation from JSON
136 const char *pkey_str;
144 * The public key of to use for lookup
146 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
149 * The public key to use for lookup
151 struct GNUNET_CRYPTO_EcdsaPublicKey pkeym;
154 * The resolver options
156 enum GNUNET_GNS_LocalOptions options;
161 struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_key;
177 * Cleanup lookup handle.
179 * @param handle Handle to clean up
182 cleanup_handle (struct LookupHandle *handle)
184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
186 if (NULL != handle->json_root)
187 json_decref (handle->json_root);
189 if (NULL != handle->name)
190 GNUNET_free (handle->name);
191 if (NULL != handle->emsg)
192 GNUNET_free (handle->emsg);
193 if (NULL != handle->el)
195 GNUNET_IDENTITY_ego_lookup_cancel (handle->el);
198 if (NULL != handle->id_op)
200 GNUNET_IDENTITY_cancel (handle->id_op);
201 handle->id_op = NULL;
203 if (NULL != handle->lookup_request)
205 GNUNET_GNS_lookup_cancel (handle->lookup_request);
206 handle->lookup_request = NULL;
208 if (NULL != handle->identity)
210 GNUNET_IDENTITY_disconnect (handle->identity);
211 handle->identity = NULL;
213 if (NULL != handle->gns)
215 GNUNET_GNS_disconnect (handle->gns);
219 if (NULL != handle->timeout_task)
221 GNUNET_SCHEDULER_cancel (handle->timeout_task);
223 GNUNET_free (handle);
228 * Task run on shutdown. Cleans up everything.
231 * @param tc scheduler context
236 struct LookupHandle *handle = cls;
237 struct MHD_Response *resp;
240 if (NULL == handle->emsg)
241 handle->emsg = GNUNET_strdup("Unknown Error");
243 GNUNET_asprintf (&json_error, "{\"error\": \"%s\"}", handle->emsg);
244 handle->response_code = MHD_HTTP_OK;
246 resp = GNUNET_REST_create_response (json_error);
247 handle->proc (handle->proc_cls, resp, handle->response_code);
248 cleanup_handle (handle);
249 GNUNET_free(json_error);
254 * Create json representation of a GNSRECORD
256 * @param rd the GNSRECORD_Data
259 gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd)
261 const char *typename;
266 typename = GNUNET_GNSRECORD_number_to_typename (rd->record_type);
267 string_val = GNUNET_GNSRECORD_value_to_string (rd->record_type,
271 if (NULL == string_val)
273 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
274 "Record of type %d malformed, skipping\n",
275 (int) rd->record_type);
278 record_obj = json_object ();
279 json_object_set_new (record_obj, "type", json_string (typename));
280 json_object_set_new (record_obj, "value", json_string (string_val));
281 GNUNET_free (string_val);
283 if (GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION & rd->flags)
285 struct GNUNET_TIME_Relative time_rel;
286 time_rel.rel_value_us = rd->expiration_time;
287 exp_str = GNUNET_STRINGS_relative_time_to_string (time_rel, 1);
291 struct GNUNET_TIME_Absolute time_abs;
292 time_abs.abs_value_us = rd->expiration_time;
293 exp_str = GNUNET_STRINGS_absolute_time_to_string (time_abs);
295 json_object_set_new (record_obj, "expiration_time", json_string (exp_str));
297 json_object_set_new (record_obj, "expired",
298 json_boolean (GNUNET_YES == GNUNET_GNSRECORD_is_expired (rd)));
303 * Function called with the result of a GNS lookup.
305 * @param cls the 'const char *' name that was resolved
306 * @param rd_count number of records returned
307 * @param rd array of @a rd_count records with the results
310 process_lookup_result (void *cls, uint32_t rd_count,
311 const struct GNUNET_GNSRECORD_Data *rd)
313 struct LookupHandle *handle = cls;
314 struct MHD_Response *resp;
317 json_t *result_array;
320 result_array = json_array();
321 handle->lookup_request = NULL;
322 for (i=0; i<rd_count; i++)
324 if ( (rd[i].record_type != handle->type) &&
325 (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
327 record_obj = gnsrecord_to_json (&(rd[i]));
328 json_array_append (result_array, record_obj);
329 json_decref (record_obj);
331 result = json_dumps(result_array, 0);
332 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
333 resp = GNUNET_REST_create_response (result);
334 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
335 GNUNET_free (result);
336 json_decref (result_array);
337 cleanup_handle (handle);
342 * Perform the actual resolution, starting with the zone
343 * identified by the given public key and the shorten zone.
345 * @param pkey public key to use for the zone, can be NULL
348 lookup_with_public_key (struct LookupHandle *handle)
350 if (UINT32_MAX == handle->type)
352 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
353 _("Invalid typename specified, assuming `ANY'\n"));
354 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
356 if (NULL != handle->name)
358 handle->lookup_request = GNUNET_GNS_lookup (handle->gns,
363 &process_lookup_result,
368 handle->emsg = GNUNET_strdup("Parameter name is missing");
369 GNUNET_SCHEDULER_add_now (&do_error, handle);
376 * Method called to with the ego we are to use for the lookup,
377 * when the ego is determined by a name.
379 * @param cls closure (NULL, unused)
380 * @param ego ego handle, NULL if not found
383 identity_zone_cb (void *cls,
384 const struct GNUNET_IDENTITY_Ego *ego)
386 struct LookupHandle *handle = cls;
391 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
392 _("Ego for not found, cannot perform lookup.\n"));
393 handle->emsg = GNUNET_strdup ("Ego for not found, cannot perform lookup.");
394 GNUNET_SCHEDULER_add_now (&do_error, handle);
399 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
400 lookup_with_public_key (handle);
402 json_decref(handle->json_root);
407 * Method called to with the ego we are to use for the lookup,
408 * when the ego is the one for the default master zone.
410 * @param cls closure (NULL, unused)
411 * @param ego ego handle, NULL if not found
412 * @param ctx context for application to store data for this ego
413 * (during the lifetime of this process, initially NULL)
414 * @param name name assigned by the user for this ego,
415 * NULL if the user just deleted the ego and it
416 * must thus no longer be used
419 identity_master_cb (void *cls,
420 struct GNUNET_IDENTITY_Ego *ego,
425 struct LookupHandle *handle = cls;
427 handle->id_op = NULL;
430 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
431 _("Ego for `gns-master' not found, cannot perform lookup. Did you run gnunet-gns-import.sh?\n"));
432 handle->emsg = GNUNET_strdup("Ego for `gns-master' not found, cannot perform lookup. Did you run gnunet-gns-import.sh?");
433 GNUNET_SCHEDULER_add_now (&do_error, handle);
436 GNUNET_IDENTITY_ego_get_public_key (ego,
438 /* main name is our own master zone, do no look for that in the DHT */
439 handle->options = GNUNET_GNS_LO_LOCAL_MASTER;
440 /* if the name is of the form 'label.gnu', never go to the DHT */
442 if (NULL != handle->name)
443 dot = strchr (handle->name, '.');
444 if ( (NULL != dot) &&
445 (0 == strcasecmp (dot, ".gnu")) )
446 handle->options = GNUNET_GNS_LO_NO_DHT;
447 lookup_with_public_key (handle);
453 * @param handle the lookup handle
456 get_gns_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
460 struct LookupHandle *handle = cls;
461 struct GNUNET_HashCode key;
465 //check for /gns otherwise 404
466 if (strlen (GNUNET_REST_API_NS_GNS) > strlen (url))
468 handle->emsg = GNUNET_strdup("Wrong URL");
469 GNUNET_SCHEDULER_add_now (&do_error, handle);
474 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
475 handle->gns = GNUNET_GNS_connect (cfg);
476 handle->identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL);
477 handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
479 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
480 if (NULL == handle->gns)
482 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Connecting to GNS failed\n");
483 handle->emsg = GNUNET_strdup("Connecting to GNS failed");
484 GNUNET_SCHEDULER_add_now (&do_error, handle);
488 //check parameter name -> BAD_REQUEST
489 GNUNET_CRYPTO_hash (GNUNET_REST_PARAMETER_GNS_NAME,
490 strlen (GNUNET_REST_PARAMETER_GNS_NAME),
493 == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
496 handle->emsg = GNUNET_strdup("Parameter name is missing");
497 GNUNET_SCHEDULER_add_now (&do_error, handle);
500 handle->name = GNUNET_strdup(GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
503 //check parameter record_type, optional
504 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE,
505 strlen (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE),
508 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
511 handle->type = GNUNET_GNSRECORD_typename_to_number(
512 GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
517 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
520 //check parameter options, optional
521 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_OPTIONS,
522 strlen (GNUNET_REST_JSONAPI_GNS_OPTIONS),
524 handle->options = GNUNET_GNS_LO_DEFAULT;
526 == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
529 temp_val = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map, &key);
530 if (1 < strlen(temp_val))
532 handle->options = GNUNET_GNS_LO_DEFAULT;
536 //atoi because no valid conversion is default local option
537 enum_test = atoi(temp_val);
539 handle->options = GNUNET_GNS_LO_DEFAULT;
541 handle->options = enum_test;
545 handle->options = GNUNET_GNS_LO_DEFAULT;
547 //check parameter pkey, shortcut to lookup
548 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_PKEY,
549 strlen (GNUNET_REST_JSONAPI_GNS_PKEY),
552 == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
555 handle->pkey_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
557 GNUNET_assert(NULL != handle->pkey_str);
559 != GNUNET_CRYPTO_ecdsa_public_key_from_string (
560 handle->pkey_str, strlen (handle->pkey_str), &(handle->pkey)))
562 handle->emsg = GNUNET_strdup("Parameter pkey has a wrong format");
563 GNUNET_SCHEDULER_add_now (&do_error, handle);
566 lookup_with_public_key (handle);
570 //check parameter ego, lookup public key of ego
571 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_EGO,
572 strlen (GNUNET_REST_JSONAPI_GNS_EGO),
575 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
578 handle->ego_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
580 handle->el = GNUNET_IDENTITY_ego_lookup (cfg,
587 //if name ends with .zkey then get public key
588 if ( (NULL != handle->name) &&
589 (strlen (handle->name) > 4) &&
590 (0 == strcmp (".zkey",
591 &handle->name[strlen (handle->name) - 4])) )
593 GNUNET_CRYPTO_ecdsa_key_get_public( GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
595 lookup_with_public_key (handle);
597 else //else use gns-master identity
599 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
607 * Handle rest request
609 * @param handle the lookup handle
612 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
616 struct MHD_Response *resp;
617 struct LookupHandle *handle = cls;
619 //For GNS, independent of path return all options
620 resp = GNUNET_REST_create_response (NULL);
621 MHD_add_response_header (resp,
622 "Access-Control-Allow-Methods",
623 MHD_HTTP_METHOD_GET);
624 handle->proc (handle->proc_cls,
627 cleanup_handle (handle);
632 * Function processing the REST call
634 * @param method HTTP method
635 * @param url URL of the HTTP request
636 * @param data body of the HTTP request (optional)
637 * @param data_size length of the body
638 * @param proc callback function for the result
639 * @param proc_cls closure for @a proc
640 * @return #GNUNET_OK if request accepted
643 rest_gns_process_request (struct GNUNET_REST_RequestHandle *conndata_handle,
644 GNUNET_REST_ResultProcessor proc,
647 static const struct GNUNET_REST_RequestHandler handlers[] = {
648 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont},
649 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont},
650 GNUNET_REST_HANDLER_END
652 struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
653 struct GNUNET_REST_RequestHandlerError err;
655 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
656 handle->proc_cls = proc_cls;
658 handle->rest_handle = conndata_handle;
660 if (GNUNET_NO == GNUNET_REST_handle_request (conndata_handle,
665 handle->response_code = err.error_code;
666 GNUNET_SCHEDULER_add_now (&do_error, handle);
672 * Entry point for the plugin.
674 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
675 * @return NULL on error, otherwise the plugin context
678 libgnunet_plugin_rest_gns_init (void *cls)
680 static struct Plugin plugin;
682 struct GNUNET_REST_Plugin *api;
684 if (NULL != plugin.cfg)
685 return NULL; /* can only initialize once! */
686 memset (&plugin, 0, sizeof (struct Plugin));
688 api = GNUNET_new (struct GNUNET_REST_Plugin);
690 api->name = GNUNET_REST_API_NS_GNS;
691 api->process_request = &rest_gns_process_request;
692 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
693 _("GNS REST API initialized\n"));
699 * Exit point from the plugin.
701 * @param cls the plugin context (as returned by "init")
702 * @return always NULL
705 libgnunet_plugin_rest_gns_done (void *cls)
707 struct GNUNET_REST_Plugin *api = cls;
708 struct Plugin *plugin = api->cls;
712 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
713 "GNS REST plugin is finished\n");
717 /* end of plugin_rest_gns.c */