2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, 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>
36 #define API_NAMESPACE "gns"
39 * @brief struct returned by the initialization function of the plugin
43 const struct GNUNET_CONFIGURATION_Handle *cfg;
46 const struct GNUNET_CONFIGURATION_Handle *cfg;
51 * Handle to GNS service.
53 struct GNUNET_GNS_Handle *gns;
56 * Desired timeout for the lookup (default is no timeout).
58 struct GNUNET_TIME_Relative timeout;
61 * Handle to lookup request
63 struct GNUNET_GNS_LookupRequest *lookup_request;
66 * Lookup an ego with the identity service.
68 struct GNUNET_IDENTITY_EgoLookup *el;
71 * Handle for identity service.
73 struct GNUNET_IDENTITY_Handle *identity;
76 * Active operation on identity service.
78 struct GNUNET_IDENTITY_Operation *id_op;
81 * ID of a task associated with the resolution process.
83 struct GNUNET_SCHEDULER_Task * timeout_task;
86 * The root of the received JSON or NULL
91 * The plugin result processor
93 GNUNET_REST_ResultProcessor proc;
96 * The closure of the result processor
101 * The name to look up
107 * In string representation from JSON
113 * In stirng representation from JSON
115 const char *pkey_str;
118 * The record type to look up
123 * The public key of to use for lookup
125 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
128 * The public key to use for lookup
130 struct GNUNET_CRYPTO_EcdsaPublicKey pkeym;
133 * The resolver options
135 enum GNUNET_GNS_LocalOptions options;
140 struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_key;
145 * Cleanup lookup handle
146 * @praram handle Handle to clean up
149 cleanup_handle (struct LookupHandle *handle)
151 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
153 if (NULL != handle->json_root)
154 json_decref (handle->json_root);
156 if (NULL != handle->name)
157 GNUNET_free (handle->name);
158 if (NULL != handle->el)
160 GNUNET_IDENTITY_ego_lookup_cancel (handle->el);
163 if (NULL != handle->id_op)
165 GNUNET_IDENTITY_cancel (handle->id_op);
166 handle->id_op = NULL;
168 if (NULL != handle->lookup_request)
170 GNUNET_GNS_lookup_cancel (handle->lookup_request);
171 handle->lookup_request = NULL;
173 if (NULL != handle->identity)
175 GNUNET_IDENTITY_disconnect (handle->identity);
176 handle->identity = NULL;
178 if (NULL != handle->gns)
180 GNUNET_GNS_disconnect (handle->gns);
184 if (NULL != handle->timeout_task)
186 GNUNET_SCHEDULER_cancel (handle->timeout_task);
188 GNUNET_free (handle);
193 * Task run on shutdown. Cleans up everything.
196 * @param tc scheduler context
200 const struct GNUNET_SCHEDULER_TaskContext *tc)
202 struct LookupHandle *handle = cls;
203 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
204 cleanup_handle (handle);
208 * Function called with the result of a GNS lookup.
210 * @param cls the 'const char *' name that was resolved
211 * @param rd_count number of records returned
212 * @param rd array of @a rd_count records with the results
215 process_lookup_result (void *cls, uint32_t rd_count,
216 const struct GNUNET_GNSRECORD_Data *rd)
218 struct LookupHandle *handle = cls;
220 const char *typename;
225 json_t *result_array;
228 result_root = json_object();
229 result_name = json_string (handle->name);
230 result_array = json_array();
231 json_object_set (result_root, "name", result_name);
232 json_decref (result_name);
233 handle->lookup_request = NULL;
235 for (i=0; i<rd_count; i++)
237 if ( (rd[i].record_type != handle->type) &&
238 (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
240 typename = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
241 string_val = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
244 if (NULL == string_val)
246 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
247 "Record %u of type %d malformed, skipping\n",
249 (int) rd[i].record_type);
254 record_obj = json_object();
255 json_object_set_new (record_obj, "type", json_string (typename));
256 json_object_set_new (record_obj, "value", json_string (string_val));
257 json_array_append (result_array, record_obj);
258 json_decref (record_obj);
260 GNUNET_free (string_val);
262 json_object_set (result_root, "query_result", result_array);
263 json_decref (result_array);
264 result = json_dumps (result_root, JSON_COMPACT);
265 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Result %s\n", result);
266 json_decref (result_root);
267 handle->proc (handle->proc_cls, result, strlen (result), GNUNET_OK);
268 GNUNET_free (result);
269 cleanup_handle (handle);
273 * Perform the actual resolution, starting with the zone
274 * identified by the given public key and the shorten zone.
276 * @param pkey public key to use for the zone, can be NULL
277 * @param shorten_key private key used for shortening, can be NULL
280 lookup_with_keys (struct LookupHandle *handle, const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key)
282 if (UINT32_MAX == handle->type)
284 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
285 _("Invalid typename specified, assuming `ANY'\n"));
286 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
288 if (NULL != handle->name)
290 handle->lookup_request = GNUNET_GNS_lookup (handle->gns,
296 &process_lookup_result,
301 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
302 _("Please specify name to lookup!\n"));
303 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
304 cleanup_handle (handle);
310 * Method called to with the ego we are to use for shortening
313 * @param cls closure contains the public key to use
314 * @param ego ego handle, NULL if not found
315 * @param ctx context for application to store data for this ego
316 * (during the lifetime of this process, initially NULL)
317 * @param name name assigned by the user for this ego,
318 * NULL if the user just deleted the ego and it
319 * must thus no longer be used
322 identity_shorten_cb (void *cls,
323 struct GNUNET_IDENTITY_Ego *ego,
327 struct LookupHandle *handle = cls;
329 handle->id_op = NULL;
331 lookup_with_keys (handle, NULL);
333 lookup_with_keys (handle,
334 GNUNET_IDENTITY_ego_get_private_key (ego));
338 * Perform the actual resolution, starting with the zone
339 * identified by the given public key.
341 * @param pkey public key to use for the zone
344 lookup_with_public_key (struct LookupHandle *handle)
346 handle->pkeym = handle->pkey;
347 GNUNET_break (NULL == handle->id_op);
348 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
350 &identity_shorten_cb,
352 if (NULL == handle->id_op)
355 lookup_with_keys (handle, NULL);
360 * Method called to with the ego we are to use for the lookup,
361 * when the ego is determined by a name.
363 * @param cls closure (NULL, unused)
364 * @param ego ego handle, NULL if not found
367 identity_zone_cb (void *cls,
368 const struct GNUNET_IDENTITY_Ego *ego)
370 struct LookupHandle *handle = cls;
375 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
376 _("Ego for not found, cannot perform lookup.\n"));
377 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
378 cleanup_handle (handle);
383 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
384 lookup_with_public_key (handle);
386 json_decref(handle->json_root);
390 * Method called to with the ego we are to use for the lookup,
391 * when the ego is the one for the default master zone.
393 * @param cls closure (NULL, unused)
394 * @param ego ego handle, NULL if not found
395 * @param ctx context for application to store data for this ego
396 * (during the lifetime of this process, initially NULL)
397 * @param name name assigned by the user for this ego,
398 * NULL if the user just deleted the ego and it
399 * must thus no longer be used
402 identity_master_cb (void *cls,
403 struct GNUNET_IDENTITY_Ego *ego,
408 struct LookupHandle *handle = cls;
410 handle->id_op = NULL;
413 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
414 _("Ego for `gns-master' not found, cannot perform lookup. Did you run gnunet-gns-import.sh?\n"));
415 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
416 cleanup_handle (handle);
419 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
420 /* main name is our own master zone, do no look for that in the DHT */
421 handle->options = GNUNET_GNS_LO_LOCAL_MASTER;
422 /* if the name is of the form 'label.gnu', never go to the DHT */
424 if (NULL != handle->name)
425 dot = strchr (handle->name, '.');
426 if ( (NULL != dot) &&
427 (0 == strcasecmp (dot, ".gnu")) )
428 handle->options = GNUNET_GNS_LO_NO_DHT;
429 lookup_with_public_key (handle);
433 * Parse REST uri for name and record type
435 * @param url Url to parse
436 * @param handle lookup handle to populate
437 * @return GNUNET_SYSERR on error
440 parse_url (const char *url, struct LookupHandle *handle)
444 char tmp_url[strlen(url)+1];
447 strcpy (tmp_url, url);
448 tok = strtok ((char*)tmp_url, "/");
450 return GNUNET_SYSERR;
451 name = strtok (NULL, "/");
453 return GNUNET_SYSERR;
454 GNUNET_asprintf (&handle->name,
457 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
458 "Got name: %s\n", handle->name);
459 type = strtok (NULL, "/");
462 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
465 handle->type = GNUNET_GNSRECORD_typename_to_number (type);
466 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
467 "Got type: %s\n", type);
472 * Parse json from REST request
474 * @param data REST data
475 * @param data_size data size
476 * @param handle Handle to populate
477 * @return GNUNET_SYSERR on error
480 parse_json (const char *data, size_t data_size, struct LookupHandle *handle)
485 json_t *options_json;
487 char term_data[data_size+1];
488 term_data[data_size] = '\0';
490 memcpy (term_data, data, data_size);
492 handle->json_root = json_loads (term_data, 0, &error);
494 if (NULL == handle->json_root)
496 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, error.text);
497 return GNUNET_SYSERR;
500 if(!json_is_object(handle->json_root))
502 return GNUNET_SYSERR;
505 ego_json = json_object_get (handle->json_root, "ego");
507 if(json_is_string(ego_json))
509 handle->ego_str = json_string_value (ego_json);
512 pkey_json = json_object_get (handle->json_root, "pkey");
513 if(json_is_string(pkey_json))
515 handle->pkey_str = json_string_value (pkey_json);
518 options_json = json_object_get (handle->json_root, "options");
519 if(json_is_integer (options_json))
521 handle->options = json_integer_value (options_json);
528 * Function processing the REST call
530 * @param method HTTP method
531 * @param url URL of the HTTP request
532 * @param data body of the HTTP request (optional)
533 * @param data_size length of the body
534 * @param proc callback function for the result
535 * @param proc_cls closure for callback function
536 * @return GNUNET_OK if request accepted
539 rest_gns_process_request(const char *method,
543 GNUNET_REST_ResultProcessor proc,
546 struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
547 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
549 //parse name and type from url
550 if (GNUNET_OK != parse_url (url, handle))
552 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing url...\n");
553 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
554 cleanup_handle (handle);
558 handle->proc_cls = proc_cls;
562 if (GNUNET_OK != parse_json (data, data_size, handle))
564 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing json...\n");
565 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
566 cleanup_handle (handle);
570 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
572 handle->gns = GNUNET_GNS_connect (cfg);
573 handle->identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL);
574 handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
576 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
579 if (NULL == handle->gns)
581 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
582 "Connecting to GNS failed\n");
583 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
584 cleanup_handle (handle);
588 if (NULL != handle->pkey_str)
591 GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->pkey_str,
592 strlen(handle->pkey_str),
595 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
596 cleanup_handle (handle);
599 lookup_with_public_key (handle);
602 if (NULL != handle->ego_str)
604 handle->el = GNUNET_IDENTITY_ego_lookup (cfg,
610 if ( (NULL != handle->name) &&
611 (strlen (handle->name) > 4) &&
612 (0 == strcmp (".zkey",
613 &handle->name[strlen (handle->name) - 4])) )
615 /* no zone required, use 'anonymous' zone */
616 GNUNET_CRYPTO_ecdsa_key_get_public
617 (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
619 lookup_with_public_key (handle);
623 GNUNET_break (NULL == handle->id_op);
624 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
628 GNUNET_assert (NULL != handle->id_op);
633 * Entry point for the plugin.
635 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
636 * @return NULL on error, otherwise the plugin context
639 libgnunet_plugin_rest_gns_init (void *cls)
641 static struct Plugin plugin;
643 struct GNUNET_REST_Plugin *api;
645 if (NULL != plugin.cfg)
646 return NULL; /* can only initialize once! */
647 memset (&plugin, 0, sizeof (struct Plugin));
649 api = GNUNET_new (struct GNUNET_REST_Plugin);
651 api->name = API_NAMESPACE;
652 api->process_request = &rest_gns_process_request;
653 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
654 _("GNS REST API initialized\n"));
660 * Exit point from the plugin.
662 * @param cls the plugin context (as returned by "init")
663 * @return always NULL
666 libgnunet_plugin_namestore_sqlite_done (void *cls)
668 struct GNUNET_REST_Plugin *api = cls;
669 struct Plugin *plugin = api->cls;
673 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
674 "GNS REST plugin is finished\n");
678 /* end of plugin_rest_gns.c */