-Merge branch 'master' of ssh://gnunet.org/gnunet into gsoc2018/rest_api
[oweals/gnunet.git] / src / gns / plugin_rest_gns.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 GNUnet e.V.
4
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.
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    Affero General Public License for more details.
14   
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/>.
17    */
18 /**
19  * @author Martin Schanzenbach
20  * @author Philippe Buschmann
21  * @file gns/plugin_rest_gns.c
22  * @brief GNUnet GNS REST plugin
23  *
24  */
25
26 #include "platform.h"
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>
36 #include <jansson.h>
37
38 #define GNUNET_REST_API_NS_GNS "/gns"
39
40 #define GNUNET_REST_JSONAPI_GNS_RECORD_TYPE "record_type"
41
42 #define GNUNET_REST_PARAMETER_GNS_NAME "name"
43
44 #define GNUNET_REST_JSONAPI_GNS_TYPEINFO "gns_name"
45
46 #define GNUNET_REST_JSONAPI_GNS_RECORD "records"
47
48 #define GNUNET_REST_JSONAPI_GNS_EGO "ego"
49
50 #define GNUNET_REST_JSONAPI_GNS_PKEY "pkey"
51
52 #define GNUNET_REST_JSONAPI_GNS_OPTIONS "options"
53
54 /**
55  * @brief struct returned by the initialization function of the plugin
56  */
57 struct Plugin
58 {
59   const struct GNUNET_CONFIGURATION_Handle *cfg;
60 };
61
62 const struct GNUNET_CONFIGURATION_Handle *cfg;
63
64 struct LookupHandle
65 {
66   /**
67    * Handle to GNS service.
68    */
69   struct GNUNET_GNS_Handle *gns;
70
71   /**
72    * Desired timeout for the lookup (default is no timeout).
73    */
74   struct GNUNET_TIME_Relative timeout;
75
76   /**
77    * Handle to lookup request
78    */
79   struct GNUNET_GNS_LookupRequest *lookup_request;
80
81   /**
82    * Handle to rest request
83    */
84   struct GNUNET_REST_RequestHandle *rest_handle;
85
86   /**
87    * Lookup an ego with the identity service.
88    */
89   struct GNUNET_IDENTITY_EgoLookup *el;
90
91   /**
92    * Handle for identity service.
93    */
94   struct GNUNET_IDENTITY_Handle *identity;
95
96   /**
97    * Active operation on identity service.
98    */
99   struct GNUNET_IDENTITY_Operation *id_op;
100
101   /**
102    * ID of a task associated with the resolution process.
103    */
104   struct GNUNET_SCHEDULER_Task * timeout_task;
105
106   /**
107    * The root of the received JSON or NULL
108    */
109   json_t *json_root;
110
111   /**
112    * The plugin result processor
113    */
114   GNUNET_REST_ResultProcessor proc;
115
116   /**
117    * The closure of the result processor
118    */
119   void *proc_cls;
120
121   /**
122    * The name to look up
123    */
124   char *name;
125
126   /**
127    * The ego to use
128    * In string representation from JSON
129    */
130   const char *ego_str;
131
132   /**
133    * The Pkey to use
134    * In string representation from JSON
135    */
136   const char *pkey_str;
137
138   /**
139    * The record type
140    */
141   int type;
142
143   /**
144    * The public key of to use for lookup
145    */
146   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
147
148   /**
149    * The public key to use for lookup
150    */
151   struct GNUNET_CRYPTO_EcdsaPublicKey pkeym;
152
153   /**
154    * The resolver options
155    */
156   enum GNUNET_GNS_LocalOptions options;
157
158   /**
159    * the shorten key
160    */
161   struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_key;
162
163   /**
164    * HTTP response code
165    */
166   int response_code;
167
168   /**
169    * HTTP response code
170    */
171   char* emsg;
172
173 };
174
175
176 /**
177  * Cleanup lookup handle.
178  *
179  * @param handle Handle to clean up
180  */
181 static void
182 cleanup_handle (struct LookupHandle *handle)
183 {
184   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
185               "Cleaning up\n");
186   if (NULL != handle->json_root)
187     json_decref (handle->json_root);
188
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)
194   {
195     GNUNET_IDENTITY_ego_lookup_cancel (handle->el);
196     handle->el = NULL;
197   }
198   if (NULL != handle->id_op)
199   {
200     GNUNET_IDENTITY_cancel (handle->id_op);
201     handle->id_op = NULL;
202   }
203   if (NULL != handle->lookup_request)
204   {
205     GNUNET_GNS_lookup_cancel (handle->lookup_request);
206     handle->lookup_request = NULL;
207   }
208   if (NULL != handle->identity)
209   {
210     GNUNET_IDENTITY_disconnect (handle->identity);
211     handle->identity = NULL;
212   }
213   if (NULL != handle->gns)
214   {
215     GNUNET_GNS_disconnect (handle->gns);
216     handle->gns = NULL;
217   }
218
219   if (NULL != handle->timeout_task)
220   {
221     GNUNET_SCHEDULER_cancel (handle->timeout_task);
222   }
223   GNUNET_free (handle);
224 }
225
226
227 /**
228  * Task run on shutdown.  Cleans up everything.
229  *
230  * @param cls unused
231  * @param tc scheduler context
232  */
233 static void
234 do_error (void *cls)
235 {
236   struct LookupHandle *handle = cls;
237   struct MHD_Response *resp;
238   char *json_error;
239
240   if (NULL == handle->emsg)
241     handle->emsg = GNUNET_strdup("Unknown Error");
242
243   GNUNET_asprintf (&json_error, "{\"error\": \"%s\"}", handle->emsg);
244   handle->response_code = MHD_HTTP_OK;
245
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);
250 }
251
252
253 /**
254  * Create json representation of a GNSRECORD
255  *
256  * @param rd the GNSRECORD_Data
257  */
258 static json_t *
259 gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd)
260 {
261   const char *typename;
262   char *string_val;
263   const char *exp_str;
264   json_t *record_obj;
265
266   typename = GNUNET_GNSRECORD_number_to_typename (rd->record_type);
267   string_val = GNUNET_GNSRECORD_value_to_string (rd->record_type,
268                                                  rd->data,
269                                                  rd->data_size);
270
271   if (NULL == string_val)
272   {
273     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
274                 "Record of type %d malformed, skipping\n",
275                 (int) rd->record_type);
276     return NULL;
277   }
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);
282
283   if (GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION & rd->flags)
284   {
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);
288   }
289   else
290   {
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);
294   }
295   json_object_set_new (record_obj, "expiration_time", json_string (exp_str));
296
297   json_object_set_new (record_obj, "expired",
298                        json_boolean (GNUNET_YES == GNUNET_GNSRECORD_is_expired (rd)));
299   return record_obj;
300 }
301
302 /**
303  * Function called with the result of a GNS lookup.
304  *
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
308  */
309 static void
310 process_lookup_result (void *cls, uint32_t rd_count,
311                        const struct GNUNET_GNSRECORD_Data *rd)
312 {
313   struct LookupHandle *handle = cls;
314   struct MHD_Response *resp;
315   uint32_t i;
316   char *result;
317   json_t *result_array;
318   json_t *record_obj;
319
320   result_array = json_array();
321   handle->lookup_request = NULL;
322   for (i=0; i<rd_count; i++)
323   {
324     if ( (rd[i].record_type != handle->type) &&
325          (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
326       continue;
327     record_obj = gnsrecord_to_json (&(rd[i]));
328     json_array_append (result_array, record_obj);
329     json_decref (record_obj);
330   }
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);
338 }
339
340
341 /**
342  * Perform the actual resolution, starting with the zone
343  * identified by the given public key and the shorten zone.
344  *
345  * @param pkey public key to use for the zone, can be NULL
346  */
347 static void
348 lookup_with_public_key (struct LookupHandle *handle)
349 {
350   if (UINT32_MAX == handle->type)
351   {
352     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
353                 _("Invalid typename specified, assuming `ANY'\n"));
354     handle->type = GNUNET_GNSRECORD_TYPE_ANY;
355   }
356   if (NULL != handle->name)
357   {
358     handle->lookup_request = GNUNET_GNS_lookup (handle->gns,
359                                                 handle->name,
360                                                 &handle->pkey,
361                                                 handle->type,
362                                                 handle->options,
363                                                 &process_lookup_result,
364                                                 handle);
365   }
366   else
367   {
368     handle->emsg = GNUNET_strdup("Parameter name is missing");
369     GNUNET_SCHEDULER_add_now (&do_error, handle);
370     return;
371   }
372 }
373
374
375 /**
376  * Method called to with the ego we are to use for the lookup,
377  * when the ego is determined by a name.
378  *
379  * @param cls closure (NULL, unused)
380  * @param ego ego handle, NULL if not found
381  */
382 static void
383 identity_zone_cb (void *cls,
384                   const struct GNUNET_IDENTITY_Ego *ego)
385 {
386   struct LookupHandle *handle = cls;
387
388   handle->el = NULL;
389   if (NULL == ego)
390   {
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);
395     return;
396   }
397   else
398   {
399     GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
400     lookup_with_public_key (handle);
401   }
402   json_decref(handle->json_root);
403 }
404
405
406 /**
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.
409  *
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
417  */
418 static void
419 identity_master_cb (void *cls,
420                     struct GNUNET_IDENTITY_Ego *ego,
421                     void **ctx,
422                     const char *name)
423 {
424   const char *dot;
425   struct LookupHandle *handle = cls;
426
427   handle->id_op = NULL;
428   if (NULL == ego)
429   {
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);
434     return;
435   }
436   GNUNET_IDENTITY_ego_get_public_key (ego,
437                                       &handle->pkey);
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 */
441   dot = NULL;
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);
448 }
449
450 /**
451  * Handle get request
452  *
453  * @param handle the lookup handle
454  */
455 static void
456 get_gns_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
457               const char* url,
458               void *cls)
459 {
460   struct LookupHandle *handle = cls;
461   struct GNUNET_HashCode key;
462   long int enum_test;
463   char *temp_val;
464
465   //check for /gns otherwise 404
466   if (strlen (GNUNET_REST_API_NS_GNS) > strlen (url))
467   {
468     handle->emsg = GNUNET_strdup("Wrong URL");
469     GNUNET_SCHEDULER_add_now (&do_error, handle);
470     return;
471   }
472
473   //connect to gns
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,
478                                                        &do_error, handle);
479   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
480   if (NULL == handle->gns)
481   {
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);
485     return;
486   }
487
488   //check parameter name -> BAD_REQUEST
489   GNUNET_CRYPTO_hash (GNUNET_REST_PARAMETER_GNS_NAME,
490                       strlen (GNUNET_REST_PARAMETER_GNS_NAME),
491                       &key);
492   if ( GNUNET_NO
493       == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
494                                                  &key))
495   {
496     handle->emsg = GNUNET_strdup("Parameter name is missing");
497     GNUNET_SCHEDULER_add_now (&do_error, handle);
498     return;
499   }
500   handle->name = GNUNET_strdup(GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
501                                                     &key));
502
503   //check parameter record_type, optional
504   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE,
505                       strlen (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE),
506                       &key);
507   if ( GNUNET_YES ==
508        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
509                                                &key) )
510   {
511     handle->type = GNUNET_GNSRECORD_typename_to_number(
512         GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
513                                           &key));
514   }
515   else
516   {
517     handle->type = GNUNET_GNSRECORD_TYPE_ANY;
518   }
519
520   //check parameter options, optional
521   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_OPTIONS,
522                       strlen (GNUNET_REST_JSONAPI_GNS_OPTIONS),
523                       &key);
524   handle->options = GNUNET_GNS_LO_DEFAULT;
525   if ( GNUNET_YES
526       == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
527                                                  &key))
528   {
529     temp_val = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map, &key);
530     if (1 < strlen(temp_val))
531     {
532       handle->options = GNUNET_GNS_LO_DEFAULT;
533     }
534     else
535     {
536       //atoi because no valid conversion is default local option
537       enum_test = atoi(temp_val);
538       if (2 < enum_test)
539         handle->options = GNUNET_GNS_LO_DEFAULT;
540       else
541         handle->options = enum_test;
542     }
543   }
544   else
545     handle->options = GNUNET_GNS_LO_DEFAULT;
546
547   //check parameter pkey, shortcut to lookup
548   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_PKEY,
549                       strlen (GNUNET_REST_JSONAPI_GNS_PKEY),
550                       &key);
551   if ( GNUNET_YES
552       == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
553                                                  &key))
554   {
555     handle->pkey_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
556                                                           &key);
557     GNUNET_assert(NULL != handle->pkey_str);
558     if (GNUNET_OK
559         != GNUNET_CRYPTO_ecdsa_public_key_from_string (
560             handle->pkey_str, strlen (handle->pkey_str), &(handle->pkey)))
561     {
562       handle->emsg = GNUNET_strdup("Parameter pkey has a wrong format");
563       GNUNET_SCHEDULER_add_now (&do_error, handle);
564       return;
565     }
566     lookup_with_public_key (handle);
567     return;
568   }
569
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),
573                       &key);
574   if ( GNUNET_YES ==
575        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
576                                                &key) )
577   {
578     handle->ego_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
579                                                          &key);
580     handle->el = GNUNET_IDENTITY_ego_lookup (cfg,
581                                              handle->ego_str,
582                                              &identity_zone_cb,
583                                              handle);
584     return;
585   }
586
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])) )
592   {
593     GNUNET_CRYPTO_ecdsa_key_get_public( GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
594                                         &(handle->pkey));
595     lookup_with_public_key (handle);
596   }
597   else //else use gns-master identity
598   {
599     handle->id_op = GNUNET_IDENTITY_get (handle->identity,
600                                          "gns-master",
601                                          &identity_master_cb,
602                                          handle);
603   }
604 }
605
606 /**
607  * Handle rest request
608  *
609  * @param handle the lookup handle
610  */
611 static void
612 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
613               const char* url,
614               void *cls)
615 {
616   struct MHD_Response *resp;
617   struct LookupHandle *handle = cls;
618
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,
625                 resp,
626                 MHD_HTTP_OK);
627   cleanup_handle (handle);
628 }
629
630
631 /**
632  * Function processing the REST call
633  *
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
641  */
642 static void
643 rest_gns_process_request (struct GNUNET_REST_RequestHandle *conndata_handle,
644                           GNUNET_REST_ResultProcessor proc,
645                           void *proc_cls)
646 {
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
651   };
652   struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
653   struct GNUNET_REST_RequestHandlerError err;
654
655   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
656   handle->proc_cls = proc_cls;
657   handle->proc = proc;
658   handle->rest_handle = conndata_handle;
659
660   if (GNUNET_NO == GNUNET_REST_handle_request (conndata_handle,
661                                                handlers,
662                                                &err,
663                                                handle))
664   {
665     handle->response_code = err.error_code;
666     GNUNET_SCHEDULER_add_now (&do_error, handle);
667   }
668 }
669
670
671 /**
672  * Entry point for the plugin.
673  *
674  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
675  * @return NULL on error, otherwise the plugin context
676  */
677 void *
678 libgnunet_plugin_rest_gns_init (void *cls)
679 {
680   static struct Plugin plugin;
681   cfg = cls;
682   struct GNUNET_REST_Plugin *api;
683
684   if (NULL != plugin.cfg)
685     return NULL;                /* can only initialize once! */
686   memset (&plugin, 0, sizeof (struct Plugin));
687   plugin.cfg = cfg;
688   api = GNUNET_new (struct GNUNET_REST_Plugin);
689   api->cls = &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"));
694   return api;
695 }
696
697
698 /**
699  * Exit point from the plugin.
700  *
701  * @param cls the plugin context (as returned by "init")
702  * @return always NULL
703  */
704 void *
705 libgnunet_plugin_rest_gns_done (void *cls)
706 {
707   struct GNUNET_REST_Plugin *api = cls;
708   struct Plugin *plugin = api->cls;
709
710   plugin->cfg = NULL;
711   GNUNET_free (api);
712   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
713               "GNS REST plugin is finished\n");
714   return NULL;
715 }
716
717 /* end of plugin_rest_gns.c */