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;
80 * ID of a task associated with the resolution process.
82 struct GNUNET_SCHEDULER_Task * timeout_task;
86 GNUNET_REST_ResultProcessor proc;
96 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
98 struct GNUNET_CRYPTO_EcdsaPublicKey pkeym;
100 enum GNUNET_GNS_LocalOptions options;
102 struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_key;
107 cleanup_handle (struct LookupHandle *handle)
109 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
111 if (NULL != handle->json_root)
112 json_decref (handle->json_root);
114 if (NULL != handle->name)
115 GNUNET_free (handle->name);
116 if (NULL != handle->el)
118 GNUNET_IDENTITY_ego_lookup_cancel (handle->el);
121 if (NULL != handle->id_op)
123 GNUNET_IDENTITY_cancel (handle->id_op);
124 handle->id_op = NULL;
126 if (NULL != handle->lookup_request)
128 GNUNET_GNS_lookup_cancel (handle->lookup_request);
129 handle->lookup_request = NULL;
131 if (NULL != handle->identity)
133 GNUNET_IDENTITY_disconnect (handle->identity);
134 handle->identity = NULL;
136 if (NULL != handle->gns)
138 GNUNET_GNS_disconnect (handle->gns);
142 if (NULL != handle->timeout_task)
144 GNUNET_SCHEDULER_cancel (handle->timeout_task);
146 GNUNET_free (handle);
151 * Task run on shutdown. Cleans up everything.
154 * @param tc scheduler context
158 const struct GNUNET_SCHEDULER_TaskContext *tc)
160 struct LookupHandle *handle = cls;
162 cleanup_handle (handle);
163 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
167 * Function called with the result of a GNS lookup.
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
174 process_lookup_result (void *cls, uint32_t rd_count,
175 const struct GNUNET_GNSRECORD_Data *rd)
177 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
179 struct LookupHandle *handle = cls;
181 const char *typename;
186 json_t *result_array;
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);
196 handle->lookup_request = NULL;
198 for (i=0; i<rd_count; i++)
200 if ( (rd[i].record_type != handle->type) &&
201 (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
203 typename = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
204 string_val = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
207 if (NULL == string_val)
209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
210 "Record %u of type %d malformed, skipping\n",
212 (int) rd[i].record_type);
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);
223 GNUNET_free (string_val);
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);
236 * Perform the actual resolution, starting with the zone
237 * identified by the given public key and the shorten zone.
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
243 lookup_with_keys (struct LookupHandle *handle, const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key)
245 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
246 "Lookup w/ keys \n");
247 if (UINT32_MAX == handle->type)
249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250 _("Invalid typename specified, assuming `ANY'\n"));
251 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
254 if (NULL != handle->name)
256 handle->lookup_request = GNUNET_GNS_lookup (handle->gns,
262 &process_lookup_result,
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);
276 * Method called to with the ego we are to use for shortening
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
288 identity_shorten_cb (void *cls,
289 struct GNUNET_IDENTITY_Ego *ego,
293 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
295 struct LookupHandle *handle = cls;
297 handle->id_op = NULL;
299 lookup_with_keys (handle, NULL);
301 lookup_with_keys (handle,
302 GNUNET_IDENTITY_ego_get_private_key (ego));
306 * Perform the actual resolution, starting with the zone
307 * identified by the given public key.
309 * @param pkey public key to use for the zone
312 lookup_with_public_key (struct LookupHandle *handle)
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,
320 &identity_shorten_cb,
322 if (NULL == handle->id_op)
325 lookup_with_keys (handle, NULL);
330 * Method called to with the ego we are to use for the lookup,
331 * when the ego is determined by a name.
333 * @param cls closure (NULL, unused)
334 * @param ego ego handle, NULL if not found
337 identity_zone_cb (void *cls,
338 const struct GNUNET_IDENTITY_Ego *ego)
340 struct LookupHandle *handle = cls;
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);
353 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
354 lookup_with_public_key (handle);
356 json_decref(handle->json_root);
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.
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
372 identity_master_cb (void *cls,
373 struct GNUNET_IDENTITY_Ego *ego,
378 struct LookupHandle *handle = cls;
380 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
382 handle->id_op = NULL;
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);
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 */
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);
405 parse_url (const char *url, struct LookupHandle *handle)
409 char tmp_url[strlen(url)+1];
410 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Parsing %s\n", url);
411 strcpy (tmp_url, url);
413 char *tok = strtok ((char*)tmp_url, "/");
417 return GNUNET_SYSERR;
419 name = strtok (NULL, "/");
422 return GNUNET_SYSERR;
424 GNUNET_asprintf (&handle->name,
428 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429 "Got name: %s\n", handle->name);
431 type = strtok (NULL, "/");
435 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
439 handle->type = GNUNET_GNSRECORD_typename_to_number (type);
441 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
442 "Got type: %s\n", type);
447 parse_json (const char *data, size_t data_size, struct LookupHandle *handle)
452 json_t *options_json;
454 char term_data[data_size+1];
455 term_data[data_size] = '\0';
457 memcpy (term_data, data, data_size);
459 handle->json_root = json_loads (term_data, 0, &error);
461 if (NULL == handle->json_root)
463 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, error.text);
464 return GNUNET_SYSERR;
467 if(!json_is_object(handle->json_root))
469 return GNUNET_SYSERR;
472 ego_json = json_object_get (handle->json_root, "ego");
474 if(json_is_string(ego_json))
476 handle->ego_str = json_string_value (ego_json);
479 pkey_json = json_object_get (handle->json_root, "pkey");
480 if(json_is_string(pkey_json))
482 handle->pkey_str = json_string_value (pkey_json);
485 options_json = json_object_get (handle->json_root, "options");
486 if(json_is_integer (options_json))
488 handle->options = json_integer_value (options_json);
495 * Function processing the REST call
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
506 rest_gns_process_request(const char *method,
510 GNUNET_REST_ResultProcessor proc,
513 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
515 struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
517 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; //TODO read from json
519 //parse name and type from url
520 if (GNUNET_OK != parse_url (url, handle))
522 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing url...\n");
523 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
524 cleanup_handle (handle);
528 handle->proc_cls = proc_cls;
533 if (GNUNET_OK != parse_json (data, data_size, handle))
535 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing json...\n");
536 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
537 cleanup_handle (handle);
541 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
543 handle->gns = GNUNET_GNS_connect (cfg);
544 handle->identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL);
546 handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
548 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
551 if (NULL == handle->gns)
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);
560 if (NULL != handle->pkey_str)
562 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
565 GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->pkey_str,
566 strlen(handle->pkey_str),
569 proc (proc_cls, NULL, 0, GNUNET_SYSERR);
570 cleanup_handle (handle);
573 lookup_with_public_key (handle);
576 if (NULL != handle->ego_str)
578 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
580 handle->el = GNUNET_IDENTITY_ego_lookup (cfg,
586 if ( (NULL != handle->name) &&
587 (strlen (handle->name) > 4) &&
588 (0 == strcmp (".zkey",
589 &handle->name[strlen (handle->name) - 4])) )
591 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
593 /* no zone required, use 'anonymous' zone */
594 GNUNET_CRYPTO_ecdsa_key_get_public
595 (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
597 lookup_with_public_key (handle);
601 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
603 GNUNET_break (NULL == handle->id_op);
604 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
608 GNUNET_assert (NULL != handle->id_op);
613 * Entry point for the plugin.
615 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
616 * @return NULL on error, otherwise the plugin context
619 libgnunet_plugin_rest_gns_init (void *cls)
621 static struct Plugin plugin;
623 struct GNUNET_REST_Plugin *api;
625 if (NULL != plugin.cfg)
626 return NULL; /* can only initialize once! */
627 memset (&plugin, 0, sizeof (struct Plugin));
629 api = GNUNET_new (struct GNUNET_REST_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"));
640 * Exit point from the plugin.
642 * @param cls the plugin context (as returned by "init")
643 * @return always NULL
646 libgnunet_plugin_namestore_sqlite_done (void *cls)
648 struct GNUNET_REST_Plugin *api = cls;
649 struct Plugin *plugin = api->cls;
653 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
654 "GNS REST plugin is finished\n");
658 /* end of plugin_rest_gns.c */