aboutsummaryrefslogtreecommitdiff
path: root/src/peerinfo-tool/plugin_rest_peerinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/peerinfo-tool/plugin_rest_peerinfo.c')
-rw-r--r--src/peerinfo-tool/plugin_rest_peerinfo.c822
1 files changed, 822 insertions, 0 deletions
diff --git a/src/peerinfo-tool/plugin_rest_peerinfo.c b/src/peerinfo-tool/plugin_rest_peerinfo.c
new file mode 100644
index 000000000..24af2c193
--- /dev/null
+++ b/src/peerinfo-tool/plugin_rest_peerinfo.c
@@ -0,0 +1,822 @@
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 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann
23 * @file peerinfo/plugin_rest_peerinfo.c
24 * @brief GNUnet Peerinfo REST plugin
25 */
26
27#include "platform.h"
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"
34#include <jansson.h>
35
36/**
37 * Peerinfo Namespace
38 */
39#define GNUNET_REST_API_NS_PEERINFO "/peerinfo"
40
41/**
42 * Peerinfo parameter peer
43 */
44#define GNUNET_REST_PEERINFO_PEER "peer"
45
46/**
47 * Peerinfo parameter friend
48 */
49#define GNUNET_REST_PEERINFO_FRIEND "friend"
50
51/**
52 * Peerinfo parameter array
53 */
54#define GNUNET_REST_PEERINFO_ARRAY "array"
55
56/**
57 * Error message Unknown Error
58 */
59#define GNUNET_REST_PEERINFO_ERROR_UNKNOWN "Unknown Error"
60
61/**
62 * How long until we time out during address lookup?
63 */
64#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
65/**
66 * The configuration handle
67 */
68const struct GNUNET_CONFIGURATION_Handle *cfg;
69
70/**
71 * HTTP methods allows for this plugin
72 */
73static char* allow_methods;
74
75/**
76 * @brief struct returned by the initialization function of the plugin
77 */
78struct Plugin
79{
80 const struct GNUNET_CONFIGURATION_Handle *cfg;
81};
82
83
84/**
85 * Record we keep for each printable address.
86 */
87struct AddressRecord
88{
89 /**
90 * Current address-to-string context (if active, otherwise NULL).
91 */
92 struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
93
94 /**
95 * Address expiration time
96 */
97 struct GNUNET_TIME_Absolute expiration;
98
99 /**
100 * Printable address.
101 */
102 char *result;
103
104 /**
105 * Print context this address record belongs to.
106 */
107 struct PrintContext *pc;
108};
109
110
111/**
112 * Structure we use to collect printable address information.
113 */
114struct PrintContext
115{
116 /**
117 * Kept in DLL.
118 */
119 struct PrintContext *next;
120
121 /**
122 * Kept in DLL.
123 */
124 struct PrintContext *prev;
125
126 /**
127 * Identity of the peer.
128 */
129 struct GNUNET_PeerIdentity peer;
130
131 /**
132 * List of printable addresses.
133 */
134 struct AddressRecord *address_list;
135
136 /**
137 * Number of completed addresses in @e address_list.
138 */
139 unsigned int num_addresses;
140
141 /**
142 * Number of addresses allocated in @e address_list.
143 */
144 unsigned int address_list_size;
145
146 /**
147 * Current offset in @e address_list (counted down).
148 */
149 unsigned int off;
150
151 /**
152 * Hello was friend only, #GNUNET_YES or #GNUNET_NO
153 */
154 int friend_only;
155
156 /**
157 * RequestHandle
158 */
159 struct RequestHandle *handle;
160
161};
162
163/**
164 * Head of list of print contexts.
165 */
166static struct PrintContext *pc_head;
167
168/**
169 * Tail of list of print contexts.
170 */
171static struct PrintContext *pc_tail;
172
173/**
174 * The request handle
175 */
176struct RequestHandle
177{
178 /**
179 * JSON temporary array
180 */
181 json_t *temp_array;
182
183 /**
184 * Expiration time string
185 */
186 char *expiration_str;
187
188 /**
189 * Address string
190 */
191 const char *address;
192
193 /**
194 * Iteration peer public key
195 */
196 char *pubkey;
197
198 /**
199 * JSON response
200 */
201 json_t *response;
202
203 /**
204 * Handle to PEERINFO it
205 */
206 struct GNUNET_PEERINFO_IteratorContext *list_it;
207
208 /**
209 * Handle to PEERINFO
210 */
211 struct GNUNET_PEERINFO_Handle *peerinfo_handle;
212
213 /**
214 * Rest connection
215 */
216 struct GNUNET_REST_RequestHandle *rest_handle;
217
218 /**
219 * Desired timeout for the lookup (default is no timeout).
220 */
221 struct GNUNET_TIME_Relative timeout;
222
223 /**
224 * ID of a task associated with the resolution process.
225 */
226 struct GNUNET_SCHEDULER_Task *timeout_task;
227
228 /**
229 * The plugin result processor
230 */
231 GNUNET_REST_ResultProcessor proc;
232
233 /**
234 * The closure of the result processor
235 */
236 void *proc_cls;
237
238 /**
239 * The url
240 */
241 char *url;
242
243 /**
244 * Error response message
245 */
246 char *emsg;
247
248 /**
249 * Reponse code
250 */
251 int response_code;
252
253};
254
255
256/**
257 * Cleanup lookup handle
258 * @param handle Handle to clean up
259 */
260static void
261cleanup_handle (void *cls)
262{
263 struct RequestHandle *handle = cls;
264
265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
266 "Cleaning up\n");
267 if (NULL != handle->timeout_task)
268 {
269 GNUNET_SCHEDULER_cancel (handle->timeout_task);
270 handle->timeout_task = NULL;
271 }
272 if (NULL != handle->url)
273 GNUNET_free (handle->url);
274 if (NULL != handle->emsg)
275 GNUNET_free (handle->emsg);
276 if (NULL != handle->address)
277 GNUNET_free ((char*)handle->address);
278 if (NULL != handle->expiration_str)
279 GNUNET_free (handle->expiration_str);
280 if (NULL != handle->pubkey)
281 GNUNET_free (handle->pubkey);
282
283 if (NULL != handle->temp_array)
284 {
285 json_decref(handle->temp_array);
286 handle->temp_array = NULL;
287 }
288 if (NULL != handle->response)
289 {
290 json_decref(handle->response);
291 handle->response = NULL;
292 }
293
294 if (NULL != handle->list_it)
295 {
296 GNUNET_PEERINFO_iterate_cancel(handle->list_it);
297 handle->list_it = NULL;
298 }
299 if (NULL != handle->peerinfo_handle)
300 {
301 GNUNET_PEERINFO_disconnect(handle->peerinfo_handle);
302 handle->peerinfo_handle = NULL;
303 }
304
305 GNUNET_free (handle);
306}
307
308
309/**
310 * Task run on errors. Reports an error and cleans up everything.
311 *
312 * @param cls the `struct RequestHandle`
313 */
314static void
315do_error (void *cls)
316{
317 struct RequestHandle *handle = cls;
318 struct MHD_Response *resp;
319 json_t *json_error = json_object();
320 char *response;
321
322 if (NULL == handle->emsg)
323 handle->emsg = GNUNET_strdup(GNUNET_REST_PEERINFO_ERROR_UNKNOWN);
324
325 json_object_set_new(json_error,"error", json_string(handle->emsg));
326
327 if (0 == handle->response_code)
328 handle->response_code = MHD_HTTP_OK;
329 response = json_dumps (json_error, 0);
330 resp = GNUNET_REST_create_response (response);
331 handle->proc (handle->proc_cls, resp, handle->response_code);
332 json_decref(json_error);
333 GNUNET_free(response);
334 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
335}
336
337
338/**
339 * Function that assembles the response.
340 *
341 * @param cls the `struct RequestHandle`
342 */
343static void
344peerinfo_list_finished (void *cls)
345{
346 struct RequestHandle *handle = cls;
347 char *result_str;
348 struct MHD_Response *resp;
349
350 if (NULL == handle->response)
351 {
352 handle->response_code = MHD_HTTP_NOT_FOUND;
353 handle->emsg = GNUNET_strdup ("No peers found");
354 GNUNET_SCHEDULER_add_now (&do_error, handle);
355 return;
356 }
357
358 result_str = json_dumps (handle->response, 0);
359 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
360 resp = GNUNET_REST_create_response (result_str);
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);
364}
365
366
367/**
368 * Iterator callback to go over all addresses and count them.
369 *
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
374 */
375static int
376count_address (void *cls,
377 const struct GNUNET_HELLO_Address *address,
378 struct GNUNET_TIME_Absolute expiration)
379{
380 struct PrintContext *pc = cls;
381
382 if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
383 {
384 return GNUNET_OK; /* ignore expired address */
385 }
386
387 pc->off++;
388 return GNUNET_OK;
389}
390
391
392/**
393 * Print the collected address information to the console and free @a pc.
394 *
395 * @param pc printing context
396 */
397static void
398dump_pc (struct PrintContext *pc)
399{
400 struct RequestHandle *handle;
401 unsigned int i;
402 json_t *response_entry;
403 json_t *temp_array;
404 json_t *object;
405 json_t *address;
406 json_t *expires;
407 json_t *friend_and_peer_json;
408 char *friend_and_peer;
409
410 temp_array = json_array();
411 response_entry = json_object();
412
413 for (i = 0; i < pc->num_addresses; i++)
414 {
415 if (NULL != pc->address_list[i].result)
416 {
417 object = json_object ();
418 address = json_string(pc->address_list[i].result);
419 expires = json_string(
420 GNUNET_STRINGS_absolute_time_to_string (pc->address_list[i].expiration));
421 json_object_set (object, "address", address);
422 json_object_set (object, "expires", expires);
423
424 json_decref(address);
425 json_decref(expires);
426
427 json_array_append(temp_array, object);
428 json_decref(object);
429 GNUNET_free (pc->address_list[i].result);
430 }
431 }
432
433 if (0 < json_array_size(temp_array))
434 {
435 GNUNET_asprintf(&friend_and_peer,
436 "%s%s",
437 (GNUNET_YES == pc->friend_only) ? "F2F:" : "",
438 GNUNET_i2s_full (&pc->peer));
439 friend_and_peer_json = json_string(friend_and_peer);
440 json_object_set(response_entry,
441 GNUNET_REST_PEERINFO_PEER,
442 friend_and_peer_json);
443 json_object_set(response_entry,
444 GNUNET_REST_PEERINFO_ARRAY,
445 temp_array);
446 json_array_append(pc->handle->response, response_entry);
447 json_decref(friend_and_peer_json);
448 GNUNET_free(friend_and_peer);
449 }
450
451 json_decref (temp_array);
452 json_decref(response_entry);
453
454 GNUNET_free_non_null (pc->address_list);
455 GNUNET_CONTAINER_DLL_remove (pc_head,
456 pc_tail,
457 pc);
458 handle = pc->handle;
459 GNUNET_free (pc);
460
461 if ( (NULL == pc_head) &&
462 (NULL == handle->list_it) )
463 {
464 GNUNET_SCHEDULER_add_now (&peerinfo_list_finished, handle);
465 }
466
467}
468
469
470/**
471 * Function to call with a human-readable format of an address
472 *
473 * @param cls closure
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
479 */
480static void
481process_resolved_address (void *cls,
482 const char *address,
483 int res)
484{
485 struct AddressRecord *ar = cls;
486 struct PrintContext *pc = ar->pc;
487
488 if (NULL != address)
489 {
490 if (0 != strlen (address))
491 {
492 if (NULL != ar->result)
493 GNUNET_free (ar->result);
494 ar->result = GNUNET_strdup (address);
495 }
496 return;
497 }
498 ar->atsc = NULL;
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));
503 pc->num_addresses++;
504 if (pc->num_addresses == pc->address_list_size)
505 dump_pc (ar->pc);
506}
507
508
509/**
510 * Iterator callback to go over all addresses.
511 *
512 * @param cls closure
513 * @param address the address
514 * @param expiration expiration time
515 * @return #GNUNET_OK to keep the address and continue
516 */
517static int
518print_address (void *cls,
519 const struct GNUNET_HELLO_Address *address,
520 struct GNUNET_TIME_Absolute expiration)
521{
522 struct PrintContext *pc = cls;
523 struct AddressRecord *ar;
524
525 if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
526 {
527 return GNUNET_OK; /* ignore expired address */
528 }
529
530 GNUNET_assert (0 < pc->off);
531 ar = &pc->address_list[--pc->off];
532 ar->pc = pc;
533 ar->expiration = expiration;
534 GNUNET_asprintf (&ar->result,
535 "%s:%u:%u",
536 address->transport_name,
537 address->address_length,
538 address->local_info);
539 ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg,
540 address,
541 GNUNET_NO,
542 TIMEOUT,
543 &process_resolved_address,
544 ar);
545 return GNUNET_OK;
546}
547
548
549/**
550 * Callback that processes each of the known HELLOs for the
551 * iteration response construction.
552 *
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
557 */
558void
559peerinfo_list_iteration(void *cls,
560 const struct GNUNET_PeerIdentity *peer,
561 const struct GNUNET_HELLO_Message *hello,
562 const char *err_msg)
563{
564 struct RequestHandle *handle = cls;
565 struct PrintContext *pc;
566 int friend_only;
567
568 if (NULL == handle->response)
569 {
570 handle->response = json_array();
571 }
572
573 if (NULL == peer)
574 {
575 handle->list_it = NULL;
576 handle->emsg = GNUNET_strdup ("Error in communication with peerinfo");
577 if (NULL != err_msg)
578 {
579 GNUNET_free(handle->emsg);
580 handle->emsg = GNUNET_strdup (err_msg);
581 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
582 }
583 if (NULL == pc_head)
584 GNUNET_SCHEDULER_add_now (&do_error, handle);
585 return;
586 }
587 if (NULL == hello)
588 return;
589
590 friend_only = GNUNET_NO;
591 if (NULL != hello)
592 friend_only = GNUNET_HELLO_is_friend_only (hello);
593
594 pc = GNUNET_new(struct PrintContext);
595 GNUNET_CONTAINER_DLL_insert (pc_head,
596 pc_tail,
597 pc);
598 pc->peer = *peer;
599 pc->friend_only = friend_only;
600 pc->handle = handle;
601 GNUNET_HELLO_iterate_addresses (hello,
602 GNUNET_NO,
603 &count_address,
604 pc);
605 if (0 == pc->off)
606 {
607 dump_pc (pc);
608 return;
609 }
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,
614 GNUNET_NO,
615 &print_address,
616 pc);
617}
618
619/**
620 * Handle peerinfo GET request
621 *
622 * @param con_handle the connection handle
623 * @param url the url
624 * @param cls the RequestHandle
625 */
626void
627peerinfo_get (struct GNUNET_REST_RequestHandle *con_handle,
628 const char* url,
629 void *cls)
630{
631 struct RequestHandle *handle = cls;
632 struct GNUNET_HashCode key;
633 const struct GNUNET_PeerIdentity *specific_peer;
634 //GNUNET_PEER_Id peer_id;
635 int include_friend_only;
636 char* include_friend_only_str;
637
638 include_friend_only = GNUNET_NO;
639 GNUNET_CRYPTO_hash (GNUNET_REST_PEERINFO_FRIEND,
640 strlen (GNUNET_REST_PEERINFO_FRIEND),
641 &key);
642 if ( GNUNET_YES
643 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
644 &key))
645 {
646 include_friend_only_str = GNUNET_CONTAINER_multihashmap_get (
647 con_handle->url_param_map, &key);
648 if (0 == strcmp(include_friend_only_str, "yes"))
649 {
650 include_friend_only = GNUNET_YES;
651 }
652 }
653
654 specific_peer = NULL;
655 GNUNET_CRYPTO_hash (GNUNET_REST_PEERINFO_PEER,
656 strlen (GNUNET_REST_PEERINFO_PEER),
657 &key);
658 if ( GNUNET_YES
659 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
660 &key))
661 {
662 //peer_id = *(unsigned int*)GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
663 //specific_peer = GNUNET_PEER_resolve2(peer_id);
664 }
665
666 handle->list_it = GNUNET_PEERINFO_iterate(handle->peerinfo_handle,
667 include_friend_only,
668 specific_peer,
669 &peerinfo_list_iteration,
670 handle);
671}
672
673
674
675/**
676 * Respond to OPTIONS request
677 *
678 * @param con_handle the connection handle
679 * @param url the url
680 * @param cls the RequestHandle
681 */
682static void
683options_cont (struct GNUNET_REST_RequestHandle *con_handle,
684 const char* url,
685 void *cls)
686{
687 struct MHD_Response *resp;
688 struct RequestHandle *handle = cls;
689
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",
694 allow_methods);
695 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
696 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
697 return;
698}
699
700
701/**
702 * Handle rest request
703 *
704 * @param handle the request handle
705 */
706static void
707init_cont (struct RequestHandle *handle)
708{
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
714 };
715
716 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
717 handlers,
718 &err,
719 handle))
720 {
721 handle->response_code = err.error_code;
722 GNUNET_SCHEDULER_add_now (&do_error, handle);
723 }
724}
725
726
727/**
728 * Function processing the REST call
729 *
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
737 */
738static void
739rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
740 GNUNET_REST_ResultProcessor proc,
741 void *proc_cls)
742{
743 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
744
745 handle->response_code = 0;
746 handle->timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60);
747 handle->proc_cls = proc_cls;
748 handle->proc = proc;
749 handle->rest_handle = rest_handle;
750
751 handle->url = GNUNET_strdup (rest_handle->url);
752 if (handle->url[strlen (handle->url)-1] == '/')
753 handle->url[strlen (handle->url)-1] = '\0';
754 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
755 handle->peerinfo_handle = GNUNET_PEERINFO_connect(cfg);
756 init_cont(handle);
757 handle->timeout_task =
758 GNUNET_SCHEDULER_add_delayed (handle->timeout,
759 &do_error,
760 handle);
761
762 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
763}
764
765
766/**
767 * Entry point for the plugin.
768 *
769 * @param cls Config info
770 * @return NULL on error, otherwise the plugin context
771 */
772void *
773libgnunet_plugin_rest_peerinfo_init (void *cls)
774{
775 static struct Plugin plugin;
776 struct GNUNET_REST_Plugin *api;
777
778 cfg = cls;
779 if (NULL != plugin.cfg)
780 return NULL; /* can only initialize once! */
781 memset (&plugin, 0, sizeof (struct Plugin));
782 plugin.cfg = cfg;
783 api = GNUNET_new (struct GNUNET_REST_Plugin);
784 api->cls = &plugin;
785 api->name = GNUNET_REST_API_NS_PEERINFO;
786 api->process_request = &rest_process_request;
787 GNUNET_asprintf (&allow_methods,
788 "%s, %s, %s, %s, %s",
789 MHD_HTTP_METHOD_GET,
790 MHD_HTTP_METHOD_POST,
791 MHD_HTTP_METHOD_PUT,
792 MHD_HTTP_METHOD_DELETE,
793 MHD_HTTP_METHOD_OPTIONS);
794
795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796 _("Peerinfo REST API initialized\n"));
797 return api;
798}
799
800
801/**
802 * Exit point from the plugin.
803 *
804 * @param cls the plugin context (as returned by "init")
805 * @return always NULL
806 */
807void *
808libgnunet_plugin_rest_peerinfo_done (void *cls)
809{
810 struct GNUNET_REST_Plugin *api = cls;
811 struct Plugin *plugin = api->cls;
812 plugin->cfg = NULL;
813
814 GNUNET_free_non_null (allow_methods);
815 GNUNET_free (api);
816 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
817 "Peerinfo REST plugin is finished\n");
818 return NULL;
819}
820
821/* end of plugin_rest_peerinfo.c */
822