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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann
23 * @file peerinfo/plugin_rest_peerinfo.c
24 * @brief GNUnet Peerinfo REST plugin
28 #include "gnunet_rest_plugin.h"
29 #include "gnunet_peerinfo_service.h"
30 #include "gnunet_transport_service.h"
31 #include "gnunet_rest_lib.h"
32 #include "gnunet_json_lib.h"
33 #include "microhttpd.h"
39 #define GNUNET_REST_API_NS_PEERINFO "/peerinfo"
42 * Peerinfo parameter peer
44 #define GNUNET_REST_PEERINFO_PEER "peer"
47 * Peerinfo parameter friend
49 #define GNUNET_REST_PEERINFO_FRIEND "friend"
52 * Peerinfo parameter array
54 #define GNUNET_REST_PEERINFO_ARRAY "array"
57 * Error message Unknown Error
59 #define GNUNET_REST_PEERINFO_ERROR_UNKNOWN "Unknown Error"
62 * How long until we time out during address lookup?
64 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
66 * The configuration handle
68 const struct GNUNET_CONFIGURATION_Handle *cfg;
71 * HTTP methods allows for this plugin
73 static char*allow_methods;
76 * @brief struct returned by the initialization function of the plugin
80 const struct GNUNET_CONFIGURATION_Handle *cfg;
85 * Record we keep for each printable address.
90 * Current address-to-string context (if active, otherwise NULL).
92 struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
95 * Address expiration time
97 struct GNUNET_TIME_Absolute expiration;
105 * Print context this address record belongs to.
107 struct PrintContext *pc;
112 * Structure we use to collect printable address information.
119 struct PrintContext *next;
124 struct PrintContext *prev;
127 * Identity of the peer.
129 struct GNUNET_PeerIdentity peer;
132 * List of printable addresses.
134 struct AddressRecord *address_list;
137 * Number of completed addresses in @e address_list.
139 unsigned int num_addresses;
142 * Number of addresses allocated in @e address_list.
144 unsigned int address_list_size;
147 * Current offset in @e address_list (counted down).
152 * Hello was friend only, #GNUNET_YES or #GNUNET_NO
159 struct RequestHandle *handle;
163 * Head of list of print contexts.
165 static struct PrintContext *pc_head;
168 * Tail of list of print contexts.
170 static struct PrintContext *pc_tail;
178 * JSON temporary array
183 * Expiration time string
185 char *expiration_str;
193 * Iteration peer public key
203 * Handle to PEERINFO it
205 struct GNUNET_PEERINFO_IteratorContext *list_it;
210 struct GNUNET_PEERINFO_Handle *peerinfo_handle;
215 struct GNUNET_REST_RequestHandle *rest_handle;
218 * Desired timeout for the lookup (default is no timeout).
220 struct GNUNET_TIME_Relative timeout;
223 * ID of a task associated with the resolution process.
225 struct GNUNET_SCHEDULER_Task *timeout_task;
228 * The plugin result processor
230 GNUNET_REST_ResultProcessor proc;
233 * The closure of the result processor
243 * Error response message
255 * Cleanup lookup handle
256 * @param handle Handle to clean up
259 cleanup_handle (void *cls)
261 struct RequestHandle *handle = cls;
263 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
265 if (NULL != handle->timeout_task)
267 GNUNET_SCHEDULER_cancel (handle->timeout_task);
268 handle->timeout_task = NULL;
270 if (NULL != handle->url)
271 GNUNET_free (handle->url);
272 if (NULL != handle->emsg)
273 GNUNET_free (handle->emsg);
274 if (NULL != handle->address)
275 GNUNET_free_nz ((char *) handle->address);
276 if (NULL != handle->expiration_str)
277 GNUNET_free (handle->expiration_str);
278 if (NULL != handle->pubkey)
279 GNUNET_free (handle->pubkey);
281 if (NULL != handle->temp_array)
283 json_decref (handle->temp_array);
284 handle->temp_array = NULL;
286 if (NULL != handle->response)
288 json_decref (handle->response);
289 handle->response = NULL;
292 if (NULL != handle->list_it)
294 GNUNET_PEERINFO_iterate_cancel (handle->list_it);
295 handle->list_it = NULL;
297 if (NULL != handle->peerinfo_handle)
299 GNUNET_PEERINFO_disconnect (handle->peerinfo_handle);
300 handle->peerinfo_handle = NULL;
303 GNUNET_free (handle);
308 * Task run on errors. Reports an error and cleans up everything.
310 * @param cls the `struct RequestHandle`
315 struct RequestHandle *handle = cls;
316 struct MHD_Response *resp;
317 json_t *json_error = json_object ();
320 if (NULL == handle->emsg)
321 handle->emsg = GNUNET_strdup (GNUNET_REST_PEERINFO_ERROR_UNKNOWN);
323 json_object_set_new (json_error, "error", json_string (handle->emsg));
325 if (0 == handle->response_code)
326 handle->response_code = MHD_HTTP_OK;
327 response = json_dumps (json_error, 0);
328 resp = GNUNET_REST_create_response (response);
329 MHD_add_response_header (resp, "Content-Type", "application/json");
330 handle->proc (handle->proc_cls, resp, handle->response_code);
331 json_decref (json_error);
332 GNUNET_free (response);
333 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
338 * Function that assembles the response.
340 * @param cls the `struct RequestHandle`
343 peerinfo_list_finished (void *cls)
345 struct RequestHandle *handle = cls;
347 struct MHD_Response *resp;
349 if (NULL == handle->response)
351 handle->response_code = MHD_HTTP_NOT_FOUND;
352 handle->emsg = GNUNET_strdup ("No peers found");
353 GNUNET_SCHEDULER_add_now (&do_error, handle);
357 result_str = json_dumps (handle->response, 0);
358 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
359 resp = GNUNET_REST_create_response (result_str);
360 MHD_add_response_header (resp, "Content-Type", "application/json");
361 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
362 GNUNET_free_non_null (result_str);
363 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
368 * Iterator callback to go over all addresses and count them.
370 * @param cls `struct PrintContext *` with `off` to increment
371 * @param address the address
372 * @param expiration expiration time
373 * @return #GNUNET_OK to keep the address and continue
376 count_address (void *cls,
377 const struct GNUNET_HELLO_Address *address,
378 struct GNUNET_TIME_Absolute expiration)
380 struct PrintContext *pc = cls;
382 if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
384 return GNUNET_OK; /* ignore expired address */
393 * Print the collected address information to the console and free @a pc.
395 * @param pc printing context
398 dump_pc (struct PrintContext *pc)
400 struct RequestHandle *handle;
402 json_t *response_entry;
407 json_t *friend_and_peer_json;
408 char *friend_and_peer;
410 temp_array = json_array ();
411 response_entry = json_object ();
413 for (i = 0; i < pc->num_addresses; i++)
415 if (NULL != pc->address_list[i].result)
417 object = json_object ();
418 address = json_string (pc->address_list[i].result);
419 expires = json_string (
420 GNUNET_STRINGS_absolute_time_to_string (
421 pc->address_list[i].expiration));
422 json_object_set (object, "address", address);
423 json_object_set (object, "expires", expires);
425 json_decref (address);
426 json_decref (expires);
428 json_array_append (temp_array, object);
429 json_decref (object);
430 GNUNET_free (pc->address_list[i].result);
434 if (0 < json_array_size (temp_array))
436 GNUNET_asprintf (&friend_and_peer,
438 (GNUNET_YES == pc->friend_only) ? "F2F:" : "",
439 GNUNET_i2s_full (&pc->peer));
440 friend_and_peer_json = json_string (friend_and_peer);
441 json_object_set (response_entry,
442 GNUNET_REST_PEERINFO_PEER,
443 friend_and_peer_json);
444 json_object_set (response_entry,
445 GNUNET_REST_PEERINFO_ARRAY,
447 json_array_append (pc->handle->response, response_entry);
448 json_decref (friend_and_peer_json);
449 GNUNET_free (friend_and_peer);
452 json_decref (temp_array);
453 json_decref (response_entry);
455 GNUNET_free_non_null (pc->address_list);
456 GNUNET_CONTAINER_DLL_remove (pc_head,
462 if ((NULL == pc_head) &&
463 (NULL == handle->list_it))
465 GNUNET_SCHEDULER_add_now (&peerinfo_list_finished, handle);
471 * Function to call with a human-readable format of an address
474 * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
475 * @param res result of the address to string conversion:
476 * if #GNUNET_OK: address was valid (conversion to
477 * string might still have failed)
478 * if #GNUNET_SYSERR: address is invalid
481 process_resolved_address (void *cls,
485 struct AddressRecord *ar = cls;
486 struct PrintContext *pc = ar->pc;
490 if (0 != strlen (address))
492 if (NULL != ar->result)
493 GNUNET_free (ar->result);
494 ar->result = GNUNET_strdup (address);
499 if (GNUNET_SYSERR == res)
500 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
501 _ ("Failure: Cannot convert address to string for peer `%s'\n"),
502 GNUNET_i2s (&ar->pc->peer));
504 if (pc->num_addresses == pc->address_list_size)
510 * Iterator callback to go over all addresses.
513 * @param address the address
514 * @param expiration expiration time
515 * @return #GNUNET_OK to keep the address and continue
518 print_address (void *cls,
519 const struct GNUNET_HELLO_Address *address,
520 struct GNUNET_TIME_Absolute expiration)
522 struct PrintContext *pc = cls;
523 struct AddressRecord *ar;
525 if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
527 return GNUNET_OK; /* ignore expired address */
530 GNUNET_assert (0 < pc->off);
531 ar = &pc->address_list[--pc->off];
533 ar->expiration = expiration;
534 GNUNET_asprintf (&ar->result,
536 address->transport_name,
537 address->address_length,
538 address->local_info);
539 ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg,
543 &process_resolved_address,
550 * Callback that processes each of the known HELLOs for the
551 * iteration response construction.
553 * @param cls closure, NULL
554 * @param peer id of the peer, NULL for last call
555 * @param hello hello message for the peer (can be NULL)
556 * @param err_msg message
559 peerinfo_list_iteration (void *cls,
560 const struct GNUNET_PeerIdentity *peer,
561 const struct GNUNET_HELLO_Message *hello,
564 struct RequestHandle *handle = cls;
565 struct PrintContext *pc;
568 if (NULL == handle->response)
570 handle->response = json_array ();
575 handle->list_it = NULL;
576 handle->emsg = GNUNET_strdup ("Error in communication with peerinfo");
579 GNUNET_free (handle->emsg);
580 handle->emsg = GNUNET_strdup (err_msg);
581 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
584 GNUNET_SCHEDULER_add_now (&do_error, handle);
590 friend_only = GNUNET_NO;
592 friend_only = GNUNET_HELLO_is_friend_only (hello);
594 pc = GNUNET_new (struct PrintContext);
595 GNUNET_CONTAINER_DLL_insert (pc_head,
599 pc->friend_only = friend_only;
601 GNUNET_HELLO_iterate_addresses (hello,
610 pc->address_list_size = pc->off;
611 pc->address_list = GNUNET_malloc (
612 sizeof(struct AddressRecord) * pc->off);
613 GNUNET_HELLO_iterate_addresses (hello,
621 * Handle peerinfo GET request
623 * @param con_handle the connection handle
625 * @param cls the RequestHandle
628 peerinfo_get (struct GNUNET_REST_RequestHandle *con_handle,
632 struct RequestHandle *handle = cls;
633 struct GNUNET_HashCode key;
634 const struct GNUNET_PeerIdentity *specific_peer;
635 // GNUNET_PEER_Id peer_id;
636 int include_friend_only;
637 char*include_friend_only_str;
639 include_friend_only = GNUNET_NO;
640 GNUNET_CRYPTO_hash (GNUNET_REST_PEERINFO_FRIEND,
641 strlen (GNUNET_REST_PEERINFO_FRIEND),
644 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
647 include_friend_only_str = GNUNET_CONTAINER_multihashmap_get (
648 con_handle->url_param_map, &key);
649 if (0 == strcmp (include_friend_only_str, "yes"))
651 include_friend_only = GNUNET_YES;
655 specific_peer = NULL;
656 GNUNET_CRYPTO_hash (GNUNET_REST_PEERINFO_PEER,
657 strlen (GNUNET_REST_PEERINFO_PEER),
660 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
663 // peer_id = *(unsigned int*)GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
664 // specific_peer = GNUNET_PEER_resolve2(peer_id);
667 handle->list_it = GNUNET_PEERINFO_iterate (handle->peerinfo_handle,
670 &peerinfo_list_iteration,
676 * Respond to OPTIONS request
678 * @param con_handle the connection handle
680 * @param cls the RequestHandle
683 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
687 struct MHD_Response *resp;
688 struct RequestHandle *handle = cls;
690 // independent of path return all options
691 resp = GNUNET_REST_create_response (NULL);
692 MHD_add_response_header (resp,
693 "Access-Control-Allow-Methods",
695 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
696 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
702 * Handle rest request
704 * @param handle the request handle
707 init_cont (struct RequestHandle *handle)
709 struct GNUNET_REST_RequestHandlerError err;
710 static const struct GNUNET_REST_RequestHandler handlers[] = {
711 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_PEERINFO, &peerinfo_get },
712 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_PEERINFO, &options_cont },
713 GNUNET_REST_HANDLER_END
716 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
721 handle->response_code = err.error_code;
722 GNUNET_SCHEDULER_add_now (&do_error, handle);
728 * Function processing the REST call
730 * @param method HTTP method
731 * @param url URL of the HTTP request
732 * @param data body of the HTTP request (optional)
733 * @param data_size length of the body
734 * @param proc callback function for the result
735 * @param proc_cls closure for callback function
736 * @return GNUNET_OK if request accepted
739 rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
740 GNUNET_REST_ResultProcessor proc,
743 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
745 handle->response_code = 0;
746 handle->timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
748 handle->proc_cls = proc_cls;
750 handle->rest_handle = rest_handle;
752 handle->url = GNUNET_strdup (rest_handle->url);
753 if (handle->url[strlen (handle->url) - 1] == '/')
754 handle->url[strlen (handle->url) - 1] = '\0';
755 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
756 handle->peerinfo_handle = GNUNET_PEERINFO_connect (cfg);
758 handle->timeout_task =
759 GNUNET_SCHEDULER_add_delayed (handle->timeout,
763 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
768 * Entry point for the plugin.
770 * @param cls Config info
771 * @return NULL on error, otherwise the plugin context
774 libgnunet_plugin_rest_peerinfo_init (void *cls)
776 static struct Plugin plugin;
777 struct GNUNET_REST_Plugin *api;
780 if (NULL != plugin.cfg)
781 return NULL; /* can only initialize once! */
782 memset (&plugin, 0, sizeof(struct Plugin));
784 api = GNUNET_new (struct GNUNET_REST_Plugin);
786 api->name = GNUNET_REST_API_NS_PEERINFO;
787 api->process_request = &rest_process_request;
788 GNUNET_asprintf (&allow_methods,
789 "%s, %s, %s, %s, %s",
791 MHD_HTTP_METHOD_POST,
793 MHD_HTTP_METHOD_DELETE,
794 MHD_HTTP_METHOD_OPTIONS);
796 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
797 _ ("Peerinfo REST API initialized\n"));
803 * Exit point from the plugin.
805 * @param cls the plugin context (as returned by "init")
806 * @return always NULL
809 libgnunet_plugin_rest_peerinfo_done (void *cls)
811 struct GNUNET_REST_Plugin *api = cls;
812 struct Plugin *plugin = api->cls;
816 GNUNET_free_non_null (allow_methods);
818 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819 "Peerinfo REST plugin is finished\n");
824 /* end of plugin_rest_peerinfo.c */