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