From 97e72521285a2ff2189334cb43d0a8f385d73e4a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 11 Aug 2018 10:40:26 +0200 Subject: fix build order: peerinfo logic depending on transport must be in peerinfo-tool --- src/peerinfo-tool/Makefile.am | 23 + src/peerinfo-tool/plugin_rest_peerinfo.c | 801 +++++++++++++++++++++++++++++++ src/peerinfo/Makefile.am | 23 +- src/peerinfo/plugin_rest_peerinfo.c | 801 ------------------------------- 4 files changed, 825 insertions(+), 823 deletions(-) create mode 100644 src/peerinfo-tool/plugin_rest_peerinfo.c delete mode 100644 src/peerinfo/plugin_rest_peerinfo.c diff --git a/src/peerinfo-tool/Makefile.am b/src/peerinfo-tool/Makefile.am index 648957a05..6f9a3360c 100644 --- a/src/peerinfo-tool/Makefile.am +++ b/src/peerinfo-tool/Makefile.am @@ -10,6 +10,8 @@ if USE_COVERAGE XLIB = -lgcov endif +plugindir = $(libdir)/gnunet + bin_PROGRAMS = \ gnunet-peerinfo @@ -24,6 +26,27 @@ gnunet_peerinfo_LDADD = \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ $(top_builddir)/src/util/libgnunetutil.la +if HAVE_MHD +if HAVE_JSON +plugin_LTLIBRARIES = \ + libgnunet_plugin_rest_peerinfo.la +endif +endif + + +libgnunet_plugin_rest_peerinfo_la_SOURCES = \ + plugin_rest_peerinfo.c +libgnunet_plugin_rest_peerinfo_la_LIBADD = \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/rest/libgnunetrest.la \ + $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ + $(LTLIBINTL) -ljansson -lmicrohttpd +libgnunet_plugin_rest_peerinfo_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + + + if HAVE_PYTHON check_SCRIPTS = \ test_gnunet_peerinfo.py diff --git a/src/peerinfo-tool/plugin_rest_peerinfo.c b/src/peerinfo-tool/plugin_rest_peerinfo.c new file mode 100644 index 000000000..97c473e36 --- /dev/null +++ b/src/peerinfo-tool/plugin_rest_peerinfo.c @@ -0,0 +1,801 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2015 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ +/** + * @author Martin Schanzenbach + * @author Philippe Buschmann + * @file peerinfo/plugin_rest_peerinfo.c + * @brief GNUnet Peerinfo REST plugin + */ + +#include "platform.h" +#include "gnunet_rest_plugin.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_transport_service.h" +#include "gnunet_rest_lib.h" +#include "gnunet_json_lib.h" +#include "microhttpd.h" +#include + +#define GNUNET_REST_API_NS_PEERINFO "/peerinfo" + +#define GNUNET_REST_API_PEERINFO_PEER "peer" +#define GNUNET_REST_API_PEERINFO_FRIEND "friend" +#define GNUNET_REST_API_PEERINFO_ARRAY "array" + +#define GNUNET_REST_ERROR_UNKNOWN "Unkown Error" + +/** + * How long until we time out during address lookup? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) +/** + * The configuration handle + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * HTTP methods allows for this plugin + */ +static char* allow_methods; + +/** + * @brief struct returned by the initialization function of the plugin + */ +struct Plugin +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; +}; + + +/** + * Record we keep for each printable address. + */ +struct AddressRecord +{ + /** + * Current address-to-string context (if active, otherwise NULL). + */ + struct GNUNET_TRANSPORT_AddressToStringContext *atsc; + + /** + * Address expiration time + */ + struct GNUNET_TIME_Absolute expiration; + + /** + * Printable address. + */ + char *result; + + /** + * Print context this address record belongs to. + */ + struct PrintContext *pc; +}; + + +/** + * Structure we use to collect printable address information. + */ +struct PrintContext +{ + + /** + * Kept in DLL. + */ + struct PrintContext *next; + + /** + * Kept in DLL. + */ + struct PrintContext *prev; + + /** + * Identity of the peer. + */ + struct GNUNET_PeerIdentity peer; + + /** + * List of printable addresses. + */ + struct AddressRecord *address_list; + + /** + * Number of completed addresses in @e address_list. + */ + unsigned int num_addresses; + + /** + * Number of addresses allocated in @e address_list. + */ + unsigned int address_list_size; + + /** + * Current offset in @e address_list (counted down). + */ + unsigned int off; + + /** + * Hello was friend only, #GNUNET_YES or #GNUNET_NO + */ + int friend_only; + + /** + * RequestHandle + */ + struct RequestHandle *handle; + +}; + +/** + * Head of list of print contexts. + */ +static struct PrintContext *pc_head; + +/** + * Tail of list of print contexts. + */ +static struct PrintContext *pc_tail; + +struct RequestHandle +{ + /** + * JSON temporary array + */ + json_t *temp_array; + + /** + * Expiration time string + */ + char *expiration_str; + + /** + * Address string + */ + const char *address; + + /** + * Iteration peer public key + */ + char *pubkey; + + /** + * JSON response + */ + json_t *response; + + /** + * Handle to PEERINFO it + */ + struct GNUNET_PEERINFO_IteratorContext *list_it; + + /** + * Handle to PEERINFO + */ + struct GNUNET_PEERINFO_Handle *peerinfo_handle; + + /** + * Rest connection + */ + struct GNUNET_REST_RequestHandle *rest_handle; + + /** + * Desired timeout for the lookup (default is no timeout). + */ + struct GNUNET_TIME_Relative timeout; + + /** + * ID of a task associated with the resolution process. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * The plugin result processor + */ + GNUNET_REST_ResultProcessor proc; + + /** + * The closure of the result processor + */ + void *proc_cls; + + /** + * The url + */ + char *url; + + /** + * Error response message + */ + char *emsg; + + /** + * Reponse code + */ + int response_code; + +}; + + +/** + * Cleanup lookup handle + * @param handle Handle to clean up + */ +static void +cleanup_handle (void *cls) +{ + struct RequestHandle *handle = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up\n"); + if (NULL != handle->timeout_task) + { + GNUNET_SCHEDULER_cancel (handle->timeout_task); + handle->timeout_task = NULL; + } + if (NULL != handle->url) + GNUNET_free (handle->url); + if (NULL != handle->emsg) + GNUNET_free (handle->emsg); + if (NULL != handle->address) + GNUNET_free ((char*)handle->address); + if (NULL != handle->expiration_str) + GNUNET_free (handle->expiration_str); + if (NULL != handle->pubkey) + GNUNET_free (handle->pubkey); + + if (NULL != handle->temp_array) + { + json_decref(handle->temp_array); + handle->temp_array = NULL; + } + if (NULL != handle->response) + { + json_decref(handle->response); + handle->response = NULL; + } + + if (NULL != handle->list_it) + { + GNUNET_PEERINFO_iterate_cancel(handle->list_it); + handle->list_it = NULL; + } + if (NULL != handle->peerinfo_handle) + { + GNUNET_PEERINFO_disconnect(handle->peerinfo_handle); + handle->peerinfo_handle = NULL; + } + + GNUNET_free (handle); +} + + +/** + * Task run on errors. Reports an error and cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + json_t *json_error = json_object(); + char *response; + + if (NULL == handle->emsg) + handle->emsg = GNUNET_strdup(GNUNET_REST_ERROR_UNKNOWN); + + json_object_set_new(json_error,"error", json_string(handle->emsg)); + + if (0 == handle->response_code) + handle->response_code = MHD_HTTP_OK; + response = json_dumps (json_error, 0); + resp = GNUNET_REST_create_response (response); + handle->proc (handle->proc_cls, resp, handle->response_code); + json_decref(json_error); + GNUNET_free(response); + GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); +} + + +/** + * Function that assembles our response. + */ +static void +peerinfo_list_finished (void *cls) +{ + struct RequestHandle *handle = cls; + char *result_str; + struct MHD_Response *resp; + + if (NULL == handle->response) + { + handle->emsg = GNUNET_strdup ("No peers found"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + result_str = json_dumps (handle->response, 0); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str); + resp = GNUNET_REST_create_response (result_str); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_free_non_null (result_str); + GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); +} + + +/** + * Iterator callback to go over all addresses and count them. + * + * @param cls `struct PrintContext *` with `off` to increment + * @param address the address + * @param expiration expiration time + * @return #GNUNET_OK to keep the address and continue + */ +static int +count_address (void *cls, + const struct GNUNET_HELLO_Address *address, + struct GNUNET_TIME_Absolute expiration) +{ + struct PrintContext *pc = cls; + + if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us) + { + return GNUNET_OK; /* ignore expired address */ + } + + pc->off++; + return GNUNET_OK; +} + + +/** + * Print the collected address information to the console and free @a pc. + * + * @param pc printing context + */ +static void +dump_pc (struct PrintContext *pc) +{ + struct RequestHandle *handle; + unsigned int i; + json_t *response_entry; + json_t *temp_array; + json_t *object; + json_t *address; + json_t *expires; + json_t *friend_and_peer_json; + char *friend_and_peer; + + temp_array = json_array(); + response_entry = json_object(); + +// printf (_("%sPeer `%s'\n"), +// (GNUNET_YES == pc->friend_only) ? "F2F: " : "", +// GNUNET_i2s_full (&pc->peer)); + for (i = 0; i < pc->num_addresses; i++) + { + if (NULL != pc->address_list[i].result) + { + object = json_object (); + address = json_string(pc->address_list[i].result); + expires = json_string( + GNUNET_STRINGS_absolute_time_to_string (pc->address_list[i].expiration)); + json_object_set (object, "address", address); + json_object_set (object, "expires", expires); + + json_decref(address); + json_decref(expires); + + json_array_append(temp_array, object); + json_decref(object); + GNUNET_free (pc->address_list[i].result); + } + } + + if (0 < json_array_size(temp_array)) + { + GNUNET_asprintf(&friend_and_peer, + "%s%s", + (GNUNET_YES == pc->friend_only) ? "F2F:" : "", + GNUNET_i2s_full (&pc->peer)); + friend_and_peer_json = json_string(friend_and_peer); + json_object_set(response_entry, + GNUNET_REST_API_PEERINFO_PEER, + friend_and_peer_json); + json_object_set(response_entry, + GNUNET_REST_API_PEERINFO_ARRAY, + temp_array); + json_array_append(pc->handle->response, response_entry); + json_decref(friend_and_peer_json); + GNUNET_free(friend_and_peer); + } + + json_decref (temp_array); + json_decref(response_entry); + + GNUNET_free_non_null (pc->address_list); + GNUNET_CONTAINER_DLL_remove (pc_head, + pc_tail, + pc); + handle = pc->handle; + GNUNET_free (pc); + + if ( (NULL == pc_head) && + (NULL == handle->list_it) ) + { + GNUNET_SCHEDULER_add_now (&peerinfo_list_finished, handle); + } + +} + + +/** + * Function to call with a human-readable format of an address + * + * @param cls closure + * @param address NULL on error, otherwise 0-terminated printable UTF-8 string + * @param res result of the address to string conversion: + * if #GNUNET_OK: address was valid (conversion to + * string might still have failed) + * if #GNUNET_SYSERR: address is invalid + */ +static void +process_resolved_address (void *cls, + const char *address, + int res) +{ + struct AddressRecord *ar = cls; + struct PrintContext *pc = ar->pc; + + if (NULL != address) + { + if (0 != strlen (address)) + { + if (NULL != ar->result) + GNUNET_free (ar->result); + ar->result = GNUNET_strdup (address); + } + return; + } + ar->atsc = NULL; + if (GNUNET_SYSERR == res) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Failure: Cannot convert address to string for peer `%s'\n"), + GNUNET_i2s (&ar->pc->peer)); + pc->num_addresses++; + if (pc->num_addresses == pc->address_list_size) + dump_pc (ar->pc); +} + + +/** + * Iterator callback to go over all addresses. + * + * @param cls closure + * @param address the address + * @param expiration expiration time + * @return #GNUNET_OK to keep the address and continue + */ +static int +print_address (void *cls, + const struct GNUNET_HELLO_Address *address, + struct GNUNET_TIME_Absolute expiration) +{ + struct PrintContext *pc = cls; + struct AddressRecord *ar; + + if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us) + { + return GNUNET_OK; /* ignore expired address */ + } + + GNUNET_assert (0 < pc->off); + ar = &pc->address_list[--pc->off]; + ar->pc = pc; + ar->expiration = expiration; + GNUNET_asprintf (&ar->result, + "%s:%u:%u", + address->transport_name, + address->address_length, + address->local_info); + ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg, + address, + GNUNET_NO, + TIMEOUT, + &process_resolved_address, + ar); + return GNUNET_OK; +} + + +/** + * Callback that processes each of the known HELLOs for the + * iteration response construction. + * + * @param cls closure, NULL + * @param peer id of the peer, NULL for last call + * @param hello hello message for the peer (can be NULL) + * @param err_msg message + */ +void +peerinfo_list_iteration(void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello, + const char *err_msg) +{ + struct RequestHandle *handle = cls; + struct PrintContext *pc; + int friend_only; + + if (NULL == handle->response) + { + handle->response = json_array(); + } + + if (NULL == peer) + { + handle->list_it = NULL; + handle->emsg = GNUNET_strdup ("Error in communication with peerinfo"); + if (NULL != err_msg) + { + GNUNET_free(handle->emsg); + handle->emsg = GNUNET_strdup (err_msg); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + } + if (NULL == pc_head) + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (NULL == hello) + return; + + friend_only = GNUNET_NO; + if (NULL != hello) + friend_only = GNUNET_HELLO_is_friend_only (hello); + + pc = GNUNET_new(struct PrintContext); + GNUNET_CONTAINER_DLL_insert (pc_head, + pc_tail, + pc); + pc->peer = *peer; + pc->friend_only = friend_only; + pc->handle = handle; + GNUNET_HELLO_iterate_addresses (hello, + GNUNET_NO, + &count_address, + pc); + if (0 == pc->off) + { + dump_pc (pc); + return; + } + pc->address_list_size = pc->off; + pc->address_list = GNUNET_malloc( + sizeof(struct AddressRecord) * pc->off); + GNUNET_HELLO_iterate_addresses (hello, + GNUNET_NO, + &print_address, + pc); +} + +/** + * Handle peerinfo GET request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +peerinfo_get (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct GNUNET_HashCode key; + const struct GNUNET_PeerIdentity *specific_peer; + GNUNET_PEER_Id peer_id; + int include_friend_only; + char* include_friend_only_str; + + include_friend_only = GNUNET_NO; + GNUNET_CRYPTO_hash (GNUNET_REST_API_PEERINFO_FRIEND, + strlen (GNUNET_REST_API_PEERINFO_FRIEND), + &key); + if ( GNUNET_YES + == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, + &key)) + { + include_friend_only_str = GNUNET_CONTAINER_multihashmap_get ( + con_handle->url_param_map, &key); + if (0 == strcmp(include_friend_only_str, "yes")) + { + include_friend_only = GNUNET_YES; + } + } + + specific_peer = NULL; + GNUNET_CRYPTO_hash (GNUNET_REST_API_PEERINFO_PEER, + strlen (GNUNET_REST_API_PEERINFO_PEER), + &key); + if ( GNUNET_YES + == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, + &key)) + { + peer_id = *(unsigned int*)GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key); + specific_peer = GNUNET_PEER_resolve2(peer_id); + } + + handle->list_it = GNUNET_PEERINFO_iterate(handle->peerinfo_handle, + include_friend_only, + specific_peer, + &peerinfo_list_iteration, + handle); +} + + + +/** + * Respond to OPTIONS request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +options_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct MHD_Response *resp; + struct RequestHandle *handle = cls; + + //independent of path return all options + resp = GNUNET_REST_create_response (NULL); + MHD_add_response_header (resp, + "Access-Control-Allow-Methods", + allow_methods); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); + return; +} + + +/** + * Handle rest request + * + * @param handle the request handle + */ +static void +init_cont (struct RequestHandle *handle) +{ + struct GNUNET_REST_RequestHandlerError err; + static const struct GNUNET_REST_RequestHandler handlers[] = { + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_PEERINFO, &peerinfo_get}, + {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_PEERINFO, &options_cont}, + GNUNET_REST_HANDLER_END + }; + + if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, + handlers, + &err, + handle)) + { + handle->response_code = err.error_code; + GNUNET_SCHEDULER_add_now (&do_error, handle); + } +} + + +/** + * Function processing the REST call + * + * @param method HTTP method + * @param url URL of the HTTP request + * @param data body of the HTTP request (optional) + * @param data_size length of the body + * @param proc callback function for the result + * @param proc_cls closure for callback function + * @return GNUNET_OK if request accepted + */ +static void +rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle, + GNUNET_REST_ResultProcessor proc, + void *proc_cls) +{ + struct RequestHandle *handle = GNUNET_new (struct RequestHandle); + + handle->response_code = 0; + handle->timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60); + handle->proc_cls = proc_cls; + handle->proc = proc; + handle->rest_handle = rest_handle; + + handle->url = GNUNET_strdup (rest_handle->url); + if (handle->url[strlen (handle->url)-1] == '/') + handle->url[strlen (handle->url)-1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n"); + handle->peerinfo_handle = GNUNET_PEERINFO_connect(cfg); + init_cont(handle); + handle->timeout_task = + GNUNET_SCHEDULER_add_delayed (handle->timeout, + &do_error, + handle); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); +} + + +/** + * Entry point for the plugin. + * + * @param cls Config info + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_rest_peerinfo_init (void *cls) +{ + static struct Plugin plugin; + struct GNUNET_REST_Plugin *api; + + cfg = cls; + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, 0, sizeof (struct Plugin)); + plugin.cfg = cfg; + api = GNUNET_new (struct GNUNET_REST_Plugin); + api->cls = &plugin; + api->name = GNUNET_REST_API_NS_PEERINFO; + api->process_request = &rest_process_request; + GNUNET_asprintf (&allow_methods, + "%s, %s, %s, %s, %s", + MHD_HTTP_METHOD_GET, + MHD_HTTP_METHOD_POST, + MHD_HTTP_METHOD_PUT, + MHD_HTTP_METHOD_DELETE, + MHD_HTTP_METHOD_OPTIONS); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Peerinfo REST API initialized\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_rest_peerinfo_done (void *cls) +{ + struct GNUNET_REST_Plugin *api = cls; + struct Plugin *plugin = api->cls; + plugin->cfg = NULL; + + GNUNET_free_non_null (allow_methods); + GNUNET_free (api); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peerinfo REST plugin is finished\n"); + return NULL; +} + +/* end of plugin_rest_peerinfo.c */ + diff --git a/src/peerinfo/Makefile.am b/src/peerinfo/Makefile.am index 5e96250b1..926b9ae94 100644 --- a/src/peerinfo/Makefile.am +++ b/src/peerinfo/Makefile.am @@ -5,8 +5,6 @@ pkgcfgdir= $(pkgdatadir)/config.d/ libexecdir= $(pkglibdir)/libexec/ -plugindir = $(libdir)/gnunet - pkgcfg_DATA = \ peerinfo.conf @@ -28,7 +26,6 @@ libgnunetpeerinfo_la_LIBADD = \ $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/json/libgnunetjson.la \ - $(top_builddir)/src/transport/libgnunettransport.la \ $(XLIB) \ $(LTLIBINTL) libgnunetpeerinfo_la_LDFLAGS = \ @@ -39,30 +36,12 @@ libgnunetpeerinfo_la_LDFLAGS = \ libexec_PROGRAMS = \ gnunet-service-peerinfo -if HAVE_MHD -if HAVE_JSON -plugin_LTLIBRARIES = \ - libgnunet_plugin_rest_peerinfo.la -endif -endif - gnunet_service_peerinfo_SOURCES = \ - gnunet-service-peerinfo.c + gnunet-service-peerinfo.c gnunet_service_peerinfo_LDADD = \ $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ $(top_builddir)/src/util/libgnunetutil.la - - -libgnunet_plugin_rest_peerinfo_la_SOURCES = \ - plugin_rest_peerinfo.c -libgnunet_plugin_rest_peerinfo_la_LIBADD = \ - libgnunetpeerinfo.la \ - $(top_builddir)/src/rest/libgnunetrest.la \ - $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ - $(LTLIBINTL) -ljansson -lmicrohttpd -libgnunet_plugin_rest_peerinfo_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) if HAVE_BENCHMARKS PEERINFO_BENCHMARKS = \ diff --git a/src/peerinfo/plugin_rest_peerinfo.c b/src/peerinfo/plugin_rest_peerinfo.c deleted file mode 100644 index 97c473e36..000000000 --- a/src/peerinfo/plugin_rest_peerinfo.c +++ /dev/null @@ -1,801 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012-2015 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - */ -/** - * @author Martin Schanzenbach - * @author Philippe Buschmann - * @file peerinfo/plugin_rest_peerinfo.c - * @brief GNUnet Peerinfo REST plugin - */ - -#include "platform.h" -#include "gnunet_rest_plugin.h" -#include "gnunet_peerinfo_service.h" -#include "gnunet_transport_service.h" -#include "gnunet_rest_lib.h" -#include "gnunet_json_lib.h" -#include "microhttpd.h" -#include - -#define GNUNET_REST_API_NS_PEERINFO "/peerinfo" - -#define GNUNET_REST_API_PEERINFO_PEER "peer" -#define GNUNET_REST_API_PEERINFO_FRIEND "friend" -#define GNUNET_REST_API_PEERINFO_ARRAY "array" - -#define GNUNET_REST_ERROR_UNKNOWN "Unkown Error" - -/** - * How long until we time out during address lookup? - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) -/** - * The configuration handle - */ -const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * HTTP methods allows for this plugin - */ -static char* allow_methods; - -/** - * @brief struct returned by the initialization function of the plugin - */ -struct Plugin -{ - const struct GNUNET_CONFIGURATION_Handle *cfg; -}; - - -/** - * Record we keep for each printable address. - */ -struct AddressRecord -{ - /** - * Current address-to-string context (if active, otherwise NULL). - */ - struct GNUNET_TRANSPORT_AddressToStringContext *atsc; - - /** - * Address expiration time - */ - struct GNUNET_TIME_Absolute expiration; - - /** - * Printable address. - */ - char *result; - - /** - * Print context this address record belongs to. - */ - struct PrintContext *pc; -}; - - -/** - * Structure we use to collect printable address information. - */ -struct PrintContext -{ - - /** - * Kept in DLL. - */ - struct PrintContext *next; - - /** - * Kept in DLL. - */ - struct PrintContext *prev; - - /** - * Identity of the peer. - */ - struct GNUNET_PeerIdentity peer; - - /** - * List of printable addresses. - */ - struct AddressRecord *address_list; - - /** - * Number of completed addresses in @e address_list. - */ - unsigned int num_addresses; - - /** - * Number of addresses allocated in @e address_list. - */ - unsigned int address_list_size; - - /** - * Current offset in @e address_list (counted down). - */ - unsigned int off; - - /** - * Hello was friend only, #GNUNET_YES or #GNUNET_NO - */ - int friend_only; - - /** - * RequestHandle - */ - struct RequestHandle *handle; - -}; - -/** - * Head of list of print contexts. - */ -static struct PrintContext *pc_head; - -/** - * Tail of list of print contexts. - */ -static struct PrintContext *pc_tail; - -struct RequestHandle -{ - /** - * JSON temporary array - */ - json_t *temp_array; - - /** - * Expiration time string - */ - char *expiration_str; - - /** - * Address string - */ - const char *address; - - /** - * Iteration peer public key - */ - char *pubkey; - - /** - * JSON response - */ - json_t *response; - - /** - * Handle to PEERINFO it - */ - struct GNUNET_PEERINFO_IteratorContext *list_it; - - /** - * Handle to PEERINFO - */ - struct GNUNET_PEERINFO_Handle *peerinfo_handle; - - /** - * Rest connection - */ - struct GNUNET_REST_RequestHandle *rest_handle; - - /** - * Desired timeout for the lookup (default is no timeout). - */ - struct GNUNET_TIME_Relative timeout; - - /** - * ID of a task associated with the resolution process. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * The plugin result processor - */ - GNUNET_REST_ResultProcessor proc; - - /** - * The closure of the result processor - */ - void *proc_cls; - - /** - * The url - */ - char *url; - - /** - * Error response message - */ - char *emsg; - - /** - * Reponse code - */ - int response_code; - -}; - - -/** - * Cleanup lookup handle - * @param handle Handle to clean up - */ -static void -cleanup_handle (void *cls) -{ - struct RequestHandle *handle = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Cleaning up\n"); - if (NULL != handle->timeout_task) - { - GNUNET_SCHEDULER_cancel (handle->timeout_task); - handle->timeout_task = NULL; - } - if (NULL != handle->url) - GNUNET_free (handle->url); - if (NULL != handle->emsg) - GNUNET_free (handle->emsg); - if (NULL != handle->address) - GNUNET_free ((char*)handle->address); - if (NULL != handle->expiration_str) - GNUNET_free (handle->expiration_str); - if (NULL != handle->pubkey) - GNUNET_free (handle->pubkey); - - if (NULL != handle->temp_array) - { - json_decref(handle->temp_array); - handle->temp_array = NULL; - } - if (NULL != handle->response) - { - json_decref(handle->response); - handle->response = NULL; - } - - if (NULL != handle->list_it) - { - GNUNET_PEERINFO_iterate_cancel(handle->list_it); - handle->list_it = NULL; - } - if (NULL != handle->peerinfo_handle) - { - GNUNET_PEERINFO_disconnect(handle->peerinfo_handle); - handle->peerinfo_handle = NULL; - } - - GNUNET_free (handle); -} - - -/** - * Task run on errors. Reports an error and cleans up everything. - * - * @param cls the `struct RequestHandle` - */ -static void -do_error (void *cls) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - json_t *json_error = json_object(); - char *response; - - if (NULL == handle->emsg) - handle->emsg = GNUNET_strdup(GNUNET_REST_ERROR_UNKNOWN); - - json_object_set_new(json_error,"error", json_string(handle->emsg)); - - if (0 == handle->response_code) - handle->response_code = MHD_HTTP_OK; - response = json_dumps (json_error, 0); - resp = GNUNET_REST_create_response (response); - handle->proc (handle->proc_cls, resp, handle->response_code); - json_decref(json_error); - GNUNET_free(response); - GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); -} - - -/** - * Function that assembles our response. - */ -static void -peerinfo_list_finished (void *cls) -{ - struct RequestHandle *handle = cls; - char *result_str; - struct MHD_Response *resp; - - if (NULL == handle->response) - { - handle->emsg = GNUNET_strdup ("No peers found"); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - result_str = json_dumps (handle->response, 0); - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str); - resp = GNUNET_REST_create_response (result_str); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_free_non_null (result_str); - GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); -} - - -/** - * Iterator callback to go over all addresses and count them. - * - * @param cls `struct PrintContext *` with `off` to increment - * @param address the address - * @param expiration expiration time - * @return #GNUNET_OK to keep the address and continue - */ -static int -count_address (void *cls, - const struct GNUNET_HELLO_Address *address, - struct GNUNET_TIME_Absolute expiration) -{ - struct PrintContext *pc = cls; - - if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us) - { - return GNUNET_OK; /* ignore expired address */ - } - - pc->off++; - return GNUNET_OK; -} - - -/** - * Print the collected address information to the console and free @a pc. - * - * @param pc printing context - */ -static void -dump_pc (struct PrintContext *pc) -{ - struct RequestHandle *handle; - unsigned int i; - json_t *response_entry; - json_t *temp_array; - json_t *object; - json_t *address; - json_t *expires; - json_t *friend_and_peer_json; - char *friend_and_peer; - - temp_array = json_array(); - response_entry = json_object(); - -// printf (_("%sPeer `%s'\n"), -// (GNUNET_YES == pc->friend_only) ? "F2F: " : "", -// GNUNET_i2s_full (&pc->peer)); - for (i = 0; i < pc->num_addresses; i++) - { - if (NULL != pc->address_list[i].result) - { - object = json_object (); - address = json_string(pc->address_list[i].result); - expires = json_string( - GNUNET_STRINGS_absolute_time_to_string (pc->address_list[i].expiration)); - json_object_set (object, "address", address); - json_object_set (object, "expires", expires); - - json_decref(address); - json_decref(expires); - - json_array_append(temp_array, object); - json_decref(object); - GNUNET_free (pc->address_list[i].result); - } - } - - if (0 < json_array_size(temp_array)) - { - GNUNET_asprintf(&friend_and_peer, - "%s%s", - (GNUNET_YES == pc->friend_only) ? "F2F:" : "", - GNUNET_i2s_full (&pc->peer)); - friend_and_peer_json = json_string(friend_and_peer); - json_object_set(response_entry, - GNUNET_REST_API_PEERINFO_PEER, - friend_and_peer_json); - json_object_set(response_entry, - GNUNET_REST_API_PEERINFO_ARRAY, - temp_array); - json_array_append(pc->handle->response, response_entry); - json_decref(friend_and_peer_json); - GNUNET_free(friend_and_peer); - } - - json_decref (temp_array); - json_decref(response_entry); - - GNUNET_free_non_null (pc->address_list); - GNUNET_CONTAINER_DLL_remove (pc_head, - pc_tail, - pc); - handle = pc->handle; - GNUNET_free (pc); - - if ( (NULL == pc_head) && - (NULL == handle->list_it) ) - { - GNUNET_SCHEDULER_add_now (&peerinfo_list_finished, handle); - } - -} - - -/** - * Function to call with a human-readable format of an address - * - * @param cls closure - * @param address NULL on error, otherwise 0-terminated printable UTF-8 string - * @param res result of the address to string conversion: - * if #GNUNET_OK: address was valid (conversion to - * string might still have failed) - * if #GNUNET_SYSERR: address is invalid - */ -static void -process_resolved_address (void *cls, - const char *address, - int res) -{ - struct AddressRecord *ar = cls; - struct PrintContext *pc = ar->pc; - - if (NULL != address) - { - if (0 != strlen (address)) - { - if (NULL != ar->result) - GNUNET_free (ar->result); - ar->result = GNUNET_strdup (address); - } - return; - } - ar->atsc = NULL; - if (GNUNET_SYSERR == res) - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Failure: Cannot convert address to string for peer `%s'\n"), - GNUNET_i2s (&ar->pc->peer)); - pc->num_addresses++; - if (pc->num_addresses == pc->address_list_size) - dump_pc (ar->pc); -} - - -/** - * Iterator callback to go over all addresses. - * - * @param cls closure - * @param address the address - * @param expiration expiration time - * @return #GNUNET_OK to keep the address and continue - */ -static int -print_address (void *cls, - const struct GNUNET_HELLO_Address *address, - struct GNUNET_TIME_Absolute expiration) -{ - struct PrintContext *pc = cls; - struct AddressRecord *ar; - - if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us) - { - return GNUNET_OK; /* ignore expired address */ - } - - GNUNET_assert (0 < pc->off); - ar = &pc->address_list[--pc->off]; - ar->pc = pc; - ar->expiration = expiration; - GNUNET_asprintf (&ar->result, - "%s:%u:%u", - address->transport_name, - address->address_length, - address->local_info); - ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg, - address, - GNUNET_NO, - TIMEOUT, - &process_resolved_address, - ar); - return GNUNET_OK; -} - - -/** - * Callback that processes each of the known HELLOs for the - * iteration response construction. - * - * @param cls closure, NULL - * @param peer id of the peer, NULL for last call - * @param hello hello message for the peer (can be NULL) - * @param err_msg message - */ -void -peerinfo_list_iteration(void *cls, - const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_HELLO_Message *hello, - const char *err_msg) -{ - struct RequestHandle *handle = cls; - struct PrintContext *pc; - int friend_only; - - if (NULL == handle->response) - { - handle->response = json_array(); - } - - if (NULL == peer) - { - handle->list_it = NULL; - handle->emsg = GNUNET_strdup ("Error in communication with peerinfo"); - if (NULL != err_msg) - { - GNUNET_free(handle->emsg); - handle->emsg = GNUNET_strdup (err_msg); - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - } - if (NULL == pc_head) - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - if (NULL == hello) - return; - - friend_only = GNUNET_NO; - if (NULL != hello) - friend_only = GNUNET_HELLO_is_friend_only (hello); - - pc = GNUNET_new(struct PrintContext); - GNUNET_CONTAINER_DLL_insert (pc_head, - pc_tail, - pc); - pc->peer = *peer; - pc->friend_only = friend_only; - pc->handle = handle; - GNUNET_HELLO_iterate_addresses (hello, - GNUNET_NO, - &count_address, - pc); - if (0 == pc->off) - { - dump_pc (pc); - return; - } - pc->address_list_size = pc->off; - pc->address_list = GNUNET_malloc( - sizeof(struct AddressRecord) * pc->off); - GNUNET_HELLO_iterate_addresses (hello, - GNUNET_NO, - &print_address, - pc); -} - -/** - * Handle peerinfo GET request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -void -peerinfo_get (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - struct RequestHandle *handle = cls; - struct GNUNET_HashCode key; - const struct GNUNET_PeerIdentity *specific_peer; - GNUNET_PEER_Id peer_id; - int include_friend_only; - char* include_friend_only_str; - - include_friend_only = GNUNET_NO; - GNUNET_CRYPTO_hash (GNUNET_REST_API_PEERINFO_FRIEND, - strlen (GNUNET_REST_API_PEERINFO_FRIEND), - &key); - if ( GNUNET_YES - == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, - &key)) - { - include_friend_only_str = GNUNET_CONTAINER_multihashmap_get ( - con_handle->url_param_map, &key); - if (0 == strcmp(include_friend_only_str, "yes")) - { - include_friend_only = GNUNET_YES; - } - } - - specific_peer = NULL; - GNUNET_CRYPTO_hash (GNUNET_REST_API_PEERINFO_PEER, - strlen (GNUNET_REST_API_PEERINFO_PEER), - &key); - if ( GNUNET_YES - == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, - &key)) - { - peer_id = *(unsigned int*)GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key); - specific_peer = GNUNET_PEER_resolve2(peer_id); - } - - handle->list_it = GNUNET_PEERINFO_iterate(handle->peerinfo_handle, - include_friend_only, - specific_peer, - &peerinfo_list_iteration, - handle); -} - - - -/** - * Respond to OPTIONS request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -options_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - struct MHD_Response *resp; - struct RequestHandle *handle = cls; - - //independent of path return all options - resp = GNUNET_REST_create_response (NULL); - MHD_add_response_header (resp, - "Access-Control-Allow-Methods", - allow_methods); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); - return; -} - - -/** - * Handle rest request - * - * @param handle the request handle - */ -static void -init_cont (struct RequestHandle *handle) -{ - struct GNUNET_REST_RequestHandlerError err; - static const struct GNUNET_REST_RequestHandler handlers[] = { - {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_PEERINFO, &peerinfo_get}, - {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_PEERINFO, &options_cont}, - GNUNET_REST_HANDLER_END - }; - - if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, - handlers, - &err, - handle)) - { - handle->response_code = err.error_code; - GNUNET_SCHEDULER_add_now (&do_error, handle); - } -} - - -/** - * Function processing the REST call - * - * @param method HTTP method - * @param url URL of the HTTP request - * @param data body of the HTTP request (optional) - * @param data_size length of the body - * @param proc callback function for the result - * @param proc_cls closure for callback function - * @return GNUNET_OK if request accepted - */ -static void -rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle, - GNUNET_REST_ResultProcessor proc, - void *proc_cls) -{ - struct RequestHandle *handle = GNUNET_new (struct RequestHandle); - - handle->response_code = 0; - handle->timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60); - handle->proc_cls = proc_cls; - handle->proc = proc; - handle->rest_handle = rest_handle; - - handle->url = GNUNET_strdup (rest_handle->url); - if (handle->url[strlen (handle->url)-1] == '/') - handle->url[strlen (handle->url)-1] = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n"); - handle->peerinfo_handle = GNUNET_PEERINFO_connect(cfg); - init_cont(handle); - handle->timeout_task = - GNUNET_SCHEDULER_add_delayed (handle->timeout, - &do_error, - handle); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); -} - - -/** - * Entry point for the plugin. - * - * @param cls Config info - * @return NULL on error, otherwise the plugin context - */ -void * -libgnunet_plugin_rest_peerinfo_init (void *cls) -{ - static struct Plugin plugin; - struct GNUNET_REST_Plugin *api; - - cfg = cls; - if (NULL != plugin.cfg) - return NULL; /* can only initialize once! */ - memset (&plugin, 0, sizeof (struct Plugin)); - plugin.cfg = cfg; - api = GNUNET_new (struct GNUNET_REST_Plugin); - api->cls = &plugin; - api->name = GNUNET_REST_API_NS_PEERINFO; - api->process_request = &rest_process_request; - GNUNET_asprintf (&allow_methods, - "%s, %s, %s, %s, %s", - MHD_HTTP_METHOD_GET, - MHD_HTTP_METHOD_POST, - MHD_HTTP_METHOD_PUT, - MHD_HTTP_METHOD_DELETE, - MHD_HTTP_METHOD_OPTIONS); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Peerinfo REST API initialized\n")); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the plugin context (as returned by "init") - * @return always NULL - */ -void * -libgnunet_plugin_rest_peerinfo_done (void *cls) -{ - struct GNUNET_REST_Plugin *api = cls; - struct Plugin *plugin = api->cls; - plugin->cfg = NULL; - - GNUNET_free_non_null (allow_methods); - GNUNET_free (api); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peerinfo REST plugin is finished\n"); - return NULL; -} - -/* end of plugin_rest_peerinfo.c */ - -- cgit v1.2.3