aboutsummaryrefslogtreecommitdiff
path: root/src/service/rest/gnunet-rest-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/rest/gnunet-rest-server.c')
-rw-r--r--src/service/rest/gnunet-rest-server.c1438
1 files changed, 1438 insertions, 0 deletions
diff --git a/src/service/rest/gnunet-rest-server.c b/src/service/rest/gnunet-rest-server.c
new file mode 100644
index 000000000..d71f8d352
--- /dev/null
+++ b/src/service/rest/gnunet-rest-server.c
@@ -0,0 +1,1438 @@
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 * @file src/rest/gnunet-rest-server.c
23 * @brief REST service for GNUnet services
24 *
25 */
26#include "platform.h"
27#include <microhttpd.h>
28#include "gnunet_util_lib.h"
29#include "gnunet_rest_plugin.h"
30#include "gnunet_mhd_compat.h"
31
32#include "config_plugin.h"
33#include "copying_plugin.h"
34#include "identity_plugin.h"
35#include "namestore_plugin.h"
36#include "gns_plugin.h"
37#if HAVE_JOSE
38#include "openid_plugin.h"
39#endif
40#include "reclaim_plugin.h"
41
42/**
43 * Default Socks5 listen port.
44 */
45#define GNUNET_REST_SERVICE_PORT 7776
46
47/**
48 * Maximum supported length for a URI.
49 * Should die. @deprecated
50 */
51#define MAX_HTTP_URI_LENGTH 2048
52
53/**
54 * Port for plaintext HTTP.
55 */
56#define HTTP_PORT 80
57
58/**
59 * Port for HTTPS.
60 */
61#define HTTPS_PORT 443
62
63/**
64 * After how long do we clean up unused MHD SSL/TLS instances?
65 */
66#define MHD_CACHE_TIMEOUT \
67 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
68
69#define GN_REST_STATE_INIT 0
70#define GN_REST_STATE_PROCESSING 1
71
72/**
73 * The task ID
74 */
75static struct GNUNET_SCHEDULER_Task *httpd_task;
76
77/**
78 * The address to bind to
79 */
80static in_addr_t address;
81
82/**
83 * The IPv6 address to bind to
84 */
85static struct in6_addr address6;
86
87/**
88 * The port the service is running on (default 7776)
89 */
90static unsigned long long port = GNUNET_REST_SERVICE_PORT;
91
92/**
93 * The listen socket of the service for IPv4
94 */
95static struct GNUNET_NETWORK_Handle *lsock4;
96
97/**
98 * The listen socket of the service for IPv6
99 */
100static struct GNUNET_NETWORK_Handle *lsock6;
101
102/**
103 * The listen task ID for IPv4
104 */
105static struct GNUNET_SCHEDULER_Task *ltask4;
106
107/**
108 * The listen task ID for IPv6
109 */
110static struct GNUNET_SCHEDULER_Task *ltask6;
111
112/**
113 * Daemon for HTTP
114 */
115static struct MHD_Daemon *httpd;
116
117/**
118 * Response we return on failures.
119 */
120static struct MHD_Response *failure_response;
121
122/**
123 * Our configuration.
124 */
125static const struct GNUNET_CONFIGURATION_Handle *cfg;
126
127/**
128 * Echo request Origin in CORS
129 */
130static int echo_origin;
131
132/**
133 * Do basic auth of user
134 */
135static int basic_auth_enabled;
136
137/**
138 * Basic auth secret
139 */
140static char *basic_auth_secret;
141
142/**
143 * User of the service
144 */
145char cuser[_POSIX_LOGIN_NAME_MAX];
146
147/**
148 * Allowed Origins (CORS)
149 */
150static char *allow_origins;
151
152/**
153 * Allowed Headers (CORS)
154 */
155static char *allow_headers;
156
157/**
158 * Allowed Credentials (CORS)
159 */
160static char *allow_credentials;
161
162/**
163 * Plugin list head
164 */
165static struct PluginListEntry *plugins_head;
166
167/**
168 * Plugin list tail
169 */
170static struct PluginListEntry *plugins_tail;
171
172/**
173 * A plugin list entry
174 */
175struct PluginListEntry
176{
177 /* DLL */
178 struct PluginListEntry *next;
179
180 /* DLL */
181 struct PluginListEntry *prev;
182
183 /**
184 * libname (to cleanup)
185 */
186 char *libname;
187
188 /**
189 * The plugin
190 */
191 void *plugin;
192
193 /**
194 * Request function
195 */
196 GNUNET_REST_ProcessingFunction process_request;
197};
198
199/**
200 * MHD Connection handle
201 */
202struct MhdConnectionHandle
203{
204 struct MHD_Connection *con;
205
206 struct MHD_Response *response;
207
208 struct GNUNET_REST_RequestHandle *data_handle;
209
210 struct MHD_PostProcessor *pp;
211
212 int status;
213
214 int state;
215};
216
217/**
218 * Accepted requests
219 */
220struct AcceptedRequest
221{
222 /**
223 * DLL
224 */
225 struct AcceptedRequest *next;
226
227 /**
228 * DLL
229 */
230 struct AcceptedRequest *prev;
231
232 /**
233 * Socket
234 */
235 struct GNUNET_NETWORK_Handle *sock;
236
237 /**
238 * Connection
239 */
240 struct MhdConnectionHandle *con_handle;
241
242 /**
243 * State
244 */
245 int socket_with_mhd;
246};
247
248/**
249 * AcceptedRequest list head
250 */
251static struct AcceptedRequest *req_list_head;
252
253/**
254 * AcceptedRequest list tail
255 */
256static struct AcceptedRequest *req_list_tail;
257
258
259/**
260 * plugins
261 */
262
263struct GNUNET_REST_Plugin *config_plugin;
264struct GNUNET_REST_Plugin *copying_plugin;
265struct GNUNET_REST_Plugin *identity_plugin;
266struct GNUNET_REST_Plugin *namestore_plugin;
267struct GNUNET_REST_Plugin *gns_plugin;
268#if HAVE_JOSE
269struct GNUNET_REST_Plugin *openid_plugin;
270#endif
271struct GNUNET_REST_Plugin *reclaim_plugin;
272
273/* ************************* Global helpers ********************* */
274
275
276/**
277 * Task run whenever HTTP server operations are pending.
278 *
279 * @param cls NULL
280 */
281static void
282do_httpd (void *cls);
283
284
285/**
286 * Run MHD now, we have extra data ready for the callback.
287 */
288static void
289run_mhd_now ()
290{
291 if (NULL != httpd_task)
292 {
293 GNUNET_SCHEDULER_cancel (httpd_task);
294 httpd_task = NULL;
295 }
296 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
297}
298
299
300/**
301 * Plugin result callback
302 *
303 * @param cls closure (MHD connection handle)
304 * @param data the data to return to the caller
305 * @param len length of the data
306 * @param status #GNUNET_OK if successful
307 */
308static void
309plugin_callback (void *cls, struct MHD_Response *resp, int status)
310{
311 struct MhdConnectionHandle *handle = cls;
312
313 handle->status = status;
314 handle->response = resp;
315 MHD_resume_connection (handle->con);
316 run_mhd_now ();
317}
318
319
320static int
321cleanup_url_map (void *cls, const struct GNUNET_HashCode *key, void *value)
322{
323 GNUNET_free (value);
324 return GNUNET_YES;
325}
326
327
328static void
329cleanup_handle (struct MhdConnectionHandle *handle)
330{
331 if (NULL != handle->response)
332 MHD_destroy_response (handle->response);
333 if (NULL != handle->data_handle)
334 {
335 if (NULL != handle->data_handle->header_param_map)
336 {
337 GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle
338 ->header_param_map,
339 &cleanup_url_map,
340 NULL);
341 GNUNET_CONTAINER_multihashmap_destroy (
342 handle->data_handle->header_param_map);
343 }
344 if (NULL != handle->data_handle->url_param_map)
345 {
346 GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle->url_param_map,
347 &cleanup_url_map,
348 NULL);
349 GNUNET_CONTAINER_multihashmap_destroy (
350 handle->data_handle->url_param_map);
351 }
352 GNUNET_free (handle->data_handle);
353 }
354 GNUNET_free (handle);
355}
356
357
358static void
359cleanup_ar (struct AcceptedRequest *ar)
360{
361 if (NULL != ar->con_handle)
362 {
363 cleanup_handle (ar->con_handle);
364 }
365 if (GNUNET_YES == ar->socket_with_mhd)
366 {
367 GNUNET_NETWORK_socket_free_memory_only_ (ar->sock);
368 }
369 else
370 {
371 GNUNET_NETWORK_socket_close (ar->sock);
372 }
373 ar->sock = NULL;
374 GNUNET_CONTAINER_DLL_remove (req_list_head,
375 req_list_tail,
376 ar);
377 GNUNET_free (ar);
378}
379
380
381static int
382header_iterator (void *cls,
383 enum MHD_ValueKind kind,
384 const char *key,
385 const char *value)
386{
387 struct GNUNET_REST_RequestHandle *handle = cls;
388 struct GNUNET_HashCode hkey;
389 char *val;
390 char *lowerkey;
391
392 lowerkey = GNUNET_strdup (key);
393 GNUNET_STRINGS_utf8_tolower (key, lowerkey);
394 GNUNET_CRYPTO_hash (lowerkey, strlen (lowerkey), &hkey);
395 GNUNET_asprintf (&val, "%s", value);
396 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
397 handle->header_param_map,
398 &hkey,
399 val,
400 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
401 {
402 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
403 "Could not load add header `%s'=%s\n",
404 lowerkey,
405 value);
406 }
407 GNUNET_free (lowerkey);
408 return MHD_YES;
409}
410
411
412static int
413url_iterator (void *cls,
414 enum MHD_ValueKind kind,
415 const char *key,
416 const char *value)
417{
418 struct GNUNET_REST_RequestHandle *handle = cls;
419 struct GNUNET_HashCode hkey;
420 char *val;
421
422 GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
423 GNUNET_asprintf (&val, "%s", value);
424 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
425 handle->url_param_map,
426 &hkey,
427 val,
428 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
429 {
430 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
431 "Could not load add url param `%s'=%s\n",
432 key,
433 value);
434 }
435 return MHD_YES;
436}
437
438
439static MHD_RESULT
440post_data_iter (void *cls,
441 enum MHD_ValueKind kind,
442 const char *key,
443 const char *filename,
444 const char *content_type,
445 const char *transfer_encoding,
446 const char *data,
447 uint64_t off,
448 size_t size)
449{
450 struct GNUNET_REST_RequestHandle *handle = cls;
451 struct GNUNET_HashCode hkey;
452 char *val;
453
454 if (MHD_POSTDATA_KIND != kind)
455 return MHD_YES;
456
457 GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
458 val = GNUNET_CONTAINER_multihashmap_get (handle->url_param_map,
459 &hkey);
460 if (NULL == val)
461 {
462 val = GNUNET_malloc (65536);
463 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
464 handle->url_param_map,
465 &hkey,
466 val,
467 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
468 {
469 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
470 "Could not add url param '%s'\n",
471 key);
472 GNUNET_free (val);
473 }
474 }
475 memcpy (val + off, data, size);
476 return MHD_YES;
477}
478
479
480/* ********************************* MHD response generation ******************* */
481
482/**
483 * Main MHD callback for handling requests.
484 *
485 * @param cls unused
486 * @param con MHD connection handle
487 * @param url the url in the request
488 * @param meth the HTTP method used ("GET", "PUT", etc.)
489 * @param ver the HTTP version string ("HTTP/1.1" for version 1.1, etc.)
490 * @param upload_data the data being uploaded (excluding HEADERS,
491 * for a POST that fits into memory and that is encoded
492 * with a supported encoding, the POST data will NOT be
493 * given in upload_data and is instead available as
494 * part of MHD_get_connection_values; very large POST
495 * data *will* be made available incrementally in
496 * upload_data)
497 * @param upload_data_size set initially to the size of the
498 * @a upload_data provided; the method must update this
499 * value to the number of bytes NOT processed;
500 * @param con_cls pointer to location where we store the 'struct Request'
501 * @return #MHD_YES if the connection was handled successfully,
502 * #MHD_NO if the socket must be closed due to a serious
503 * error while handling the request
504 */
505static MHD_RESULT
506create_response (void *cls,
507 struct MHD_Connection *con,
508 const char *url,
509 const char *meth,
510 const char *ver,
511 const char *upload_data,
512 size_t *upload_data_size,
513 void **con_cls)
514{
515 char *origin;
516 char *pw;
517 char *user;
518 struct AcceptedRequest *ar;
519 struct GNUNET_HashCode key;
520 struct MhdConnectionHandle *con_handle;
521 struct GNUNET_REST_RequestHandle *rest_conndata_handle;
522 struct PluginListEntry *ple;
523
524 ar = *con_cls;
525 if (NULL == ar)
526 {
527 GNUNET_break (0);
528 return MHD_NO;
529 }
530
531 if (NULL == ar->con_handle)
532 {
533 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New connection %s\n", url);
534 con_handle = GNUNET_new (struct MhdConnectionHandle);
535 con_handle->con = con;
536 con_handle->state = GN_REST_STATE_INIT;
537 ar->con_handle = con_handle;
538 return MHD_YES;
539 }
540 con_handle = ar->con_handle;
541 if (GN_REST_STATE_INIT == con_handle->state)
542 {
543 rest_conndata_handle = GNUNET_new (struct GNUNET_REST_RequestHandle);
544 rest_conndata_handle->method = meth;
545 rest_conndata_handle->url = url;
546 rest_conndata_handle->data = upload_data;
547 rest_conndata_handle->data_size = *upload_data_size;
548 rest_conndata_handle->url_param_map =
549 GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
550 rest_conndata_handle->header_param_map =
551 GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
552 con_handle->data_handle = rest_conndata_handle;
553 MHD_get_connection_values (con,
554 MHD_GET_ARGUMENT_KIND,
555 (MHD_KeyValueIterator) & url_iterator,
556 rest_conndata_handle);
557 MHD_get_connection_values (con,
558 MHD_HEADER_KIND,
559 (MHD_KeyValueIterator) & header_iterator,
560 rest_conndata_handle);
561 if (GNUNET_YES == basic_auth_enabled)
562 {
563 pw = NULL;
564 user = MHD_basic_auth_get_username_password (con, &pw);
565 if ((NULL == user) ||
566 (0 != strcmp (user, cuser)))
567 {
568 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
569 "Unknown user %s\n", user);
570 MHD_queue_basic_auth_fail_response (con, "gnunet", failure_response);
571 return MHD_YES;
572 }
573 if ((NULL == pw) ||
574 (0 != strcmp (pw, basic_auth_secret)))
575 {
576 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
577 "Password incorrect\n");
578 MHD_queue_basic_auth_fail_response (con, "gnunet", failure_response);
579 GNUNET_free (pw);
580 return MHD_YES;
581 }
582 GNUNET_free (pw);
583 }
584
585 con_handle->pp = MHD_create_post_processor (con,
586 65536,
587 &post_data_iter,
588 rest_conndata_handle);
589 if (*upload_data_size)
590 {
591 MHD_post_process (con_handle->pp, upload_data, *upload_data_size);
592 }
593 MHD_destroy_post_processor (con_handle->pp);
594
595 con_handle->state = GN_REST_STATE_PROCESSING;
596 for (ple = plugins_head; NULL != ple; ple = ple->next)
597 {
598 if (GNUNET_YES == ple->process_request (ple->plugin,
599 rest_conndata_handle,
600 &plugin_callback,
601 con_handle))
602 break; /* Request handled */
603 }
604 if (NULL == ple)
605 {
606 /** Request not handled **/
607 MHD_queue_response (con, MHD_HTTP_NOT_FOUND, failure_response);
608 }
609 *upload_data_size = 0;
610 run_mhd_now ();
611 return MHD_YES;
612 }
613 if (NULL == con_handle->response)
614 {
615 // Suspend connection until plugin is done
616 MHD_suspend_connection (con_handle->con);
617 return MHD_YES;
618 }
619 // MHD_resume_connection (con_handle->con);
620 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
621 "Queueing response from plugin with MHD\n");
622 // Handle Preflights for extensions
623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking origin\n");
624 GNUNET_CRYPTO_hash ("origin", strlen ("origin"), &key);
625 origin = GNUNET_CONTAINER_multihashmap_get (con_handle->data_handle
626 ->header_param_map,
627 &key);
628 if (NULL != origin)
629 {
630 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Origin: %s\n", origin);
631 // Only echo for browser plugins
632 if (GNUNET_YES == echo_origin)
633 {
634 if ((0 ==
635 strncmp ("moz-extension://", origin, strlen ("moz-extension://"))) ||
636 (0 == strncmp ("chrome-extension://",
637 origin,
638 strlen ("chrome-extension://"))))
639 {
640 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
641 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
642 origin));
643 }
644 }
645 if (NULL != allow_origins)
646 {
647 char *tmp = GNUNET_strdup (allow_origins);
648 char *allow_origin = strtok (tmp, ",");
649 while (NULL != allow_origin)
650 {
651 if (0 == strncmp (allow_origin, origin, strlen (allow_origin)))
652 {
653 GNUNET_assert (MHD_NO != MHD_add_response_header (
654 con_handle->response,
655 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
656 allow_origin));
657 break;
658 }
659 allow_origin = strtok (NULL, ",");
660 }
661 GNUNET_free (tmp);
662 }
663 }
664 if (NULL != allow_credentials)
665 {
666 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
667 "Access-Control-Allow-Credentials",
668 allow_credentials));
669 }
670 if (NULL != allow_headers)
671 {
672 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
673 "Access-Control-Allow-Headers",
674 allow_headers));
675 }
676 run_mhd_now ();
677 {
678 MHD_RESULT ret = MHD_queue_response (con,
679 con_handle->status,
680 con_handle->response);
681 // cleanup_handle (con_handle);
682 return ret;
683 }
684}
685
686
687/* ******************** MHD HTTP setup and event loop ******************** */
688
689
690/**
691 * Kill the MHD daemon.
692 */
693static void
694kill_httpd ()
695{
696 if (NULL != httpd)
697 {
698 MHD_stop_daemon (httpd);
699 httpd = NULL;
700 }
701 if (NULL != httpd_task)
702 {
703 GNUNET_SCHEDULER_cancel (httpd_task);
704 httpd_task = NULL;
705 }
706 if (NULL != ltask4)
707 {
708 GNUNET_SCHEDULER_cancel (ltask4);
709 ltask4 = NULL;
710 }
711 if (NULL != ltask6)
712 {
713 GNUNET_SCHEDULER_cancel (ltask6);
714 ltask6 = NULL;
715 }
716
717 if (NULL != lsock4)
718 {
719 GNUNET_NETWORK_socket_close (lsock4);
720 lsock4 = NULL;
721 }
722 if (NULL != lsock6)
723 {
724 GNUNET_NETWORK_socket_close (lsock6);
725 lsock6 = NULL;
726 }
727}
728
729
730/**
731 * Schedule MHD. This function should be called initially when an
732 * MHD is first getting its client socket, and will then automatically
733 * always be called later whenever there is work to be done.
734 *
735 * @param hd the daemon to schedule
736 */
737static void
738schedule_httpd ()
739{
740 fd_set rs;
741 fd_set ws;
742 fd_set es;
743 struct GNUNET_NETWORK_FDSet *wrs;
744 struct GNUNET_NETWORK_FDSet *wws;
745 int max;
746 int haveto;
747 MHD_UNSIGNED_LONG_LONG timeout;
748 struct GNUNET_TIME_Relative tv;
749
750 FD_ZERO (&rs);
751 FD_ZERO (&ws);
752 FD_ZERO (&es);
753 max = -1;
754 if (MHD_YES != MHD_get_fdset (httpd, &rs, &ws, &es, &max))
755 {
756 kill_httpd ();
757 return;
758 }
759 haveto = MHD_get_timeout (httpd, &timeout);
760 if (MHD_YES == haveto)
761 tv.rel_value_us = (uint64_t) timeout * 1000LL;
762 else
763 tv = GNUNET_TIME_UNIT_FOREVER_REL;
764 if (-1 != max)
765 {
766 wrs = GNUNET_NETWORK_fdset_create ();
767 wws = GNUNET_NETWORK_fdset_create ();
768 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
769 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
770 }
771 else
772 {
773 wrs = NULL;
774 wws = NULL;
775 }
776 if (NULL != httpd_task)
777 {
778 GNUNET_SCHEDULER_cancel (httpd_task);
779 httpd_task = NULL;
780 }
781 if ((MHD_YES == haveto) || (-1 != max))
782 {
783 httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
784 tv,
785 wrs,
786 wws,
787 &do_httpd,
788 NULL);
789 }
790 if (NULL != wrs)
791 GNUNET_NETWORK_fdset_destroy (wrs);
792 if (NULL != wws)
793 GNUNET_NETWORK_fdset_destroy (wws);
794}
795
796
797/**
798 * Function called when MHD first processes an incoming connection.
799 * Gives us the respective URI information.
800 *
801 * We use this to associate the `struct MHD_Connection` with our
802 * internal `struct AcceptedRequest` data structure (by checking
803 * for matching sockets).
804 *
805 * @param cls the HTTP server handle (a `struct MhdHttpList`)
806 * @param url the URL that is being requested
807 * @param connection MHD connection object for the request
808 * @return the `struct Socks5Request` that this @a connection is for
809 */
810static void *
811mhd_log_callback (void *cls,
812 const char *url,
813 struct MHD_Connection *connection)
814{
815 struct AcceptedRequest *ar;
816 const union MHD_ConnectionInfo *ci;
817
818 ci = MHD_get_connection_info (connection,
819 MHD_CONNECTION_INFO_SOCKET_CONTEXT);
820 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing %s\n", url);
821 if (NULL == ci)
822 {
823 GNUNET_break (0);
824 return NULL;
825 }
826 ar = ci->socket_context;
827 return ar;
828}
829
830
831/**
832 * Function called when MHD decides that we are done with a connection.
833 *
834 * @param cls NULL
835 * @param connection connection handle
836 * @param con_cls value as set by the last call to
837 * the MHD_AccessHandlerCallback, should be our handle
838 * @param toe reason for request termination (ignored)
839 */
840static void
841mhd_completed_cb (void *cls,
842 struct MHD_Connection *connection,
843 void **con_cls,
844 enum MHD_RequestTerminationCode toe)
845{
846 struct AcceptedRequest *ar = *con_cls;
847 if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
848 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
849 "MHD encountered error handling request: %d\n",
850 toe);
851 if (NULL == ar)
852 return;
853 if (NULL != ar->con_handle)
854 {
855 cleanup_handle (ar->con_handle);
856 ar->con_handle = NULL;
857 }
858 ar->socket_with_mhd = GNUNET_YES;
859 *con_cls = NULL;
860}
861
862
863/**
864 * Function called when MHD connection is opened or closed.
865 *
866 * @param cls NULL
867 * @param connection connection handle
868 * @param con_cls value as set by the last call to
869 * the MHD_AccessHandlerCallback, should be our `struct Socks5Request *`
870 * @param toe connection notification type
871 */
872static void
873mhd_connection_cb (void *cls,
874 struct MHD_Connection *connection,
875 void **con_cls,
876 enum MHD_ConnectionNotificationCode cnc)
877{
878 struct AcceptedRequest *ar;
879 const union MHD_ConnectionInfo *ci;
880 int sock;
881
882 switch (cnc)
883 {
884 case MHD_CONNECTION_NOTIFY_STARTED:
885 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection started...\n");
886 ci = MHD_get_connection_info (connection,
887 MHD_CONNECTION_INFO_CONNECTION_FD);
888 if (NULL == ci)
889 {
890 GNUNET_break (0);
891 return;
892 }
893 sock = ci->connect_fd;
894 for (ar = req_list_head; NULL != ar; ar = ar->next)
895 {
896 if (GNUNET_NETWORK_get_fd (ar->sock) == sock)
897 {
898 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
899 "Context set...\n");
900 *con_cls = ar;
901 break;
902 }
903 }
904 break;
905
906 case MHD_CONNECTION_NOTIFY_CLOSED:
907 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
908 "Connection closed... cleaning up\n");
909 ar = *con_cls;
910 if (NULL == ar)
911 {
912 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
913 "Connection stale!\n");
914 return;
915 }
916 cleanup_ar (ar);
917 *con_cls = NULL;
918 break;
919
920 default:
921 GNUNET_break (0);
922 }
923}
924
925
926/**
927 * Task run whenever HTTP server operations are pending.
928 *
929 * @param cls NULL
930 */
931static void
932do_httpd (void *cls)
933{
934 httpd_task = NULL;
935 MHD_run (httpd);
936 schedule_httpd ();
937}
938
939
940/**
941 * Accept new incoming connections
942 *
943 * @param cls the closure with the lsock4 or lsock6
944 */
945static void
946do_accept (void *cls)
947{
948 struct GNUNET_NETWORK_Handle *lsock = cls;
949 struct AcceptedRequest *ar;
950 int fd;
951 const struct sockaddr *addr;
952 socklen_t len;
953
954 GNUNET_assert (NULL != lsock);
955 if (lsock == lsock4)
956 {
957 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
958 lsock,
959 &do_accept,
960 lsock);
961 }
962 else if (lsock == lsock6)
963 {
964 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
965 lsock,
966 &do_accept,
967 lsock);
968 }
969 else
970 GNUNET_assert (0);
971 ar = GNUNET_new (struct AcceptedRequest);
972 ar->socket_with_mhd = GNUNET_YES;
973 ar->sock = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
974 if (NULL == ar->sock)
975 {
976 GNUNET_free (ar);
977 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
978 return;
979 }
980 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
981 "Got an inbound connection, waiting for data\n");
982 fd = GNUNET_NETWORK_get_fd (ar->sock);
983 addr = GNUNET_NETWORK_get_addr (ar->sock);
984 len = GNUNET_NETWORK_get_addrlen (ar->sock);
985 GNUNET_CONTAINER_DLL_insert (req_list_head,
986 req_list_tail,
987 ar);
988 if (MHD_YES != MHD_add_connection (httpd, fd, addr, len))
989 {
990 GNUNET_NETWORK_socket_close (ar->sock);
991 GNUNET_free (ar);
992 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
993 _ ("Failed to pass client to MHD\n"));
994 return;
995 }
996 schedule_httpd ();
997}
998
999
1000/**
1001 * Task run on shutdown
1002 *
1003 * @param cls closure
1004 */
1005static void
1006do_shutdown (void *cls)
1007{
1008 struct PluginListEntry *ple;
1009
1010 while (NULL != plugins_head)
1011 {
1012 ple = plugins_head;
1013 GNUNET_CONTAINER_DLL_remove (plugins_head,
1014 plugins_tail,
1015 ple);
1016 GNUNET_free (ple->libname);
1017 GNUNET_free (ple);
1018 }
1019 REST_config_done (config_plugin);
1020 REST_copying_done (copying_plugin);
1021 REST_identity_done (identity_plugin);
1022 REST_gns_done (gns_plugin);
1023#if HAVE_JOSE
1024 REST_openid_done (openid_plugin);
1025#endif
1026 REST_reclaim_done (reclaim_plugin);
1027 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down...\n");
1028 kill_httpd ();
1029 GNUNET_free (allow_credentials);
1030 GNUNET_free (allow_headers);
1031 MHD_destroy_response (failure_response);
1032}
1033
1034
1035/**
1036 * Create an IPv4 listen socket bound to our port.
1037 *
1038 * @return NULL on error
1039 */
1040static struct GNUNET_NETWORK_Handle *
1041bind_v4 ()
1042{
1043 struct GNUNET_NETWORK_Handle *ls;
1044 struct sockaddr_in sa4;
1045 int eno;
1046
1047 memset (&sa4, 0, sizeof(sa4));
1048 sa4.sin_family = AF_INET;
1049 sa4.sin_port = htons (port);
1050 sa4.sin_addr.s_addr = address;
1051#if HAVE_SOCKADDR_IN_SIN_LEN
1052 sa4.sin_len = sizeof(sa4);
1053#endif
1054 ls = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
1055 if (NULL == ls)
1056 return NULL;
1057 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls,
1058 (const struct sockaddr *) &sa4,
1059 sizeof(sa4)))
1060 {
1061 eno = errno;
1062 GNUNET_NETWORK_socket_close (ls);
1063 errno = eno;
1064 return NULL;
1065 }
1066 return ls;
1067}
1068
1069
1070/**
1071 * Create an IPv6 listen socket bound to our port.
1072 *
1073 * @return NULL on error
1074 */
1075static struct GNUNET_NETWORK_Handle *
1076bind_v6 ()
1077{
1078 struct GNUNET_NETWORK_Handle *ls;
1079 struct sockaddr_in6 sa6;
1080 int eno;
1081
1082 memset (&sa6, 0, sizeof(sa6));
1083 sa6.sin6_family = AF_INET6;
1084 sa6.sin6_port = htons (port);
1085 sa6.sin6_addr = address6;
1086#if HAVE_SOCKADDR_IN_SIN_LEN
1087 sa6.sin6_len = sizeof(sa6);
1088#endif
1089 ls = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_STREAM, 0);
1090 if (NULL == ls)
1091 return NULL;
1092 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls,
1093 (const struct sockaddr *) &sa6,
1094 sizeof(sa6)))
1095 {
1096 eno = errno;
1097 GNUNET_NETWORK_socket_close (ls);
1098 errno = eno;
1099 return NULL;
1100 }
1101 return ls;
1102}
1103
1104
1105/**
1106 * Callback for plugin load
1107 *
1108 * @param cls NULL
1109 * @param libname the name of the library loaded
1110 * @param lib_ret the object returned by the plugin initializer
1111 */
1112static enum GNUNET_GenericReturnValue
1113setup_plugin (const char *name,
1114 GNUNET_REST_ProcessingFunction proc,
1115 void *plugin_cls)
1116{
1117 struct PluginListEntry *ple;
1118
1119 if (NULL == plugin_cls)
1120 {
1121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1122 "Could not load plugin\n");
1123 return GNUNET_SYSERR;
1124 }
1125 GNUNET_assert (1 < strlen (name));
1126 GNUNET_assert ('/' == *name);
1127 ple = GNUNET_new (struct PluginListEntry);
1128 ple->libname = GNUNET_strdup (name);
1129 ple->plugin = plugin_cls;
1130 ple->process_request = proc;
1131 GNUNET_CONTAINER_DLL_insert (plugins_head,
1132 plugins_tail,
1133 ple);
1134 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded plugin `%s'\n", name);
1135 return GNUNET_OK;
1136}
1137
1138
1139/**
1140 * Main function that will be run
1141 *
1142 * @param cls closure
1143 * @param args remaining command-line arguments
1144 * @param cfgfile name of the configuration file used (for saving, can be NULL)
1145 * @param c configuration
1146 */
1147static void
1148run (void *cls,
1149 char *const *args,
1150 const char *cfgfile,
1151 const struct GNUNET_CONFIGURATION_Handle *c)
1152{
1153 static const char *err_page = "{}";
1154 char *addr_str;
1155 char *basic_auth_file;
1156 uint64_t secret;
1157
1158 cfg = c;
1159 plugins_head = NULL;
1160 plugins_tail = NULL;
1161 failure_response = MHD_create_response_from_buffer (strlen (err_page),
1162 (void *) err_page,
1163 MHD_RESPMEM_PERSISTENT);
1164 /* Get port to bind to */
1165 if (GNUNET_OK !=
1166 GNUNET_CONFIGURATION_get_value_number (cfg, "rest", "HTTP_PORT", &port))
1167 {
1168 // No address specified
1169 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Using default port...\n");
1170 port = GNUNET_REST_SERVICE_PORT;
1171 }
1172
1173 /* Get address to bind to */
1174 if (GNUNET_OK !=
1175 GNUNET_CONFIGURATION_get_value_string (cfg, "rest", "BIND_TO", &addr_str))
1176 {
1177 // No address specified
1178 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Don't know what to bind to...\n");
1179 GNUNET_SCHEDULER_shutdown ();
1180 return;
1181 }
1182 if (1 != inet_pton (AF_INET, addr_str, &address))
1183 {
1184 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1185 "Unable to parse address %s\n",
1186 addr_str);
1187 GNUNET_free (addr_str);
1188 GNUNET_SCHEDULER_shutdown ();
1189 return;
1190 }
1191 GNUNET_free (addr_str);
1192 /* Get address to bind to */
1193 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
1194 "rest",
1195 "BIND_TO6",
1196 &addr_str))
1197 {
1198 // No address specified
1199 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Don't know what to bind6 to...\n");
1200 GNUNET_SCHEDULER_shutdown ();
1201 return;
1202 }
1203 if (1 != inet_pton (AF_INET6, addr_str, &address6))
1204 {
1205 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1206 "Unable to parse IPv6 address %s\n",
1207 addr_str);
1208 GNUNET_free (addr_str);
1209 GNUNET_SCHEDULER_shutdown ();
1210 return;
1211 }
1212 GNUNET_free (addr_str);
1213
1214 basic_auth_enabled = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1215 "rest",
1216 "BASIC_AUTH_ENABLED");
1217 if (basic_auth_enabled)
1218 {
1219 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1220 "rest",
1221 "BASIC_AUTH_SECRET_FILE",
1222 &basic_auth_file))
1223 {
1224 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1225 "No basic auth secret file location set...\n");
1226 GNUNET_SCHEDULER_shutdown ();
1227 return;
1228 }
1229 if (GNUNET_YES != GNUNET_DISK_file_test (basic_auth_file))
1230 {
1231 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1232 "No basic auth secret found... generating\n");
1233 secret = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1234 UINT64_MAX);
1235 basic_auth_secret = GNUNET_STRINGS_data_to_string_alloc (&secret,
1236 sizeof(secret));
1237 if (GNUNET_OK !=
1238 GNUNET_DISK_fn_write (basic_auth_file,
1239 basic_auth_secret,
1240 strlen (basic_auth_secret),
1241 GNUNET_DISK_PERM_USER_READ
1242 | GNUNET_DISK_PERM_USER_WRITE))
1243 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1244 "write",
1245 basic_auth_file);
1246 GNUNET_free (basic_auth_file);
1247 }
1248 else
1249 {
1250 char basic_auth_secret_tmp[16]; // Should be more than enough
1251 memset (basic_auth_secret_tmp, 0, 16);
1252 if (GNUNET_SYSERR == GNUNET_DISK_fn_read (basic_auth_file,
1253 basic_auth_secret_tmp,
1254 sizeof (basic_auth_secret_tmp)
1255 - 1))
1256 {
1257 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1258 "Unable to read basic auth secret file.\n");
1259 GNUNET_SCHEDULER_shutdown ();
1260 GNUNET_free (basic_auth_file);
1261 return;
1262 }
1263 GNUNET_free (basic_auth_file);
1264 if (0 != getlogin_r (cuser, _POSIX_LOGIN_NAME_MAX))
1265 {
1266 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1267 "Unable to get user.\n");
1268 GNUNET_SCHEDULER_shutdown ();
1269 return;
1270 }
1271 basic_auth_secret = GNUNET_strdup (basic_auth_secret_tmp);
1272 }
1273 }
1274
1275 /* Get CORS data from cfg */
1276 echo_origin =
1277 GNUNET_CONFIGURATION_get_value_yesno (cfg,
1278 "rest",
1279 "REST_ECHO_ORIGIN_WEBEXT");
1280 allow_origins = NULL;
1281 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
1282 "rest",
1283 "REST_ALLOW_ORIGIN",
1284 &allow_origins))
1285 {
1286 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1287 "No CORS Access-Control-Allow-Origin header will be sent...\n");
1288 }
1289 if (GNUNET_OK !=
1290 GNUNET_CONFIGURATION_get_value_string (cfg,
1291 "rest",
1292 "REST_ALLOW_CREDENTIALS",
1293 &allow_credentials))
1294 {
1295 // No origin specified
1296 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1297 "No CORS Credential Header will be sent...\n");
1298 }
1299
1300 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
1301 "rest",
1302 "REST_ALLOW_HEADERS",
1303 &allow_headers))
1304 {
1305 // No origin specified
1306 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1307 "No CORS Access-Control-Allow-Headers Header will be sent...\n");
1308 }
1309
1310/* Open listen socket proxy */
1311 lsock6 = bind_v6 ();
1312 if (NULL == lsock6)
1313 {
1314 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
1315 }
1316 else
1317 {
1318 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
1319 {
1320 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
1321 GNUNET_NETWORK_socket_close (lsock6);
1322 lsock6 = NULL;
1323 }
1324 else
1325 {
1326 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1327 lsock6,
1328 &do_accept,
1329 lsock6);
1330 }
1331 }
1332 lsock4 = bind_v4 ();
1333 if (NULL == lsock4)
1334 {
1335 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
1336 }
1337 else
1338 {
1339 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
1340 {
1341 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
1342 GNUNET_NETWORK_socket_close (lsock4);
1343 lsock4 = NULL;
1344 }
1345 else
1346 {
1347 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1348 lsock4,
1349 &do_accept,
1350 lsock4);
1351 }
1352 }
1353 if ((NULL == lsock4) && (NULL == lsock6))
1354 {
1355 GNUNET_SCHEDULER_shutdown ();
1356 return;
1357 }
1358 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service listens on port %llu\n",
1359 port);
1360 httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET
1361 | MHD_ALLOW_SUSPEND_RESUME,
1362 0,
1363 NULL,
1364 NULL,
1365 &create_response,
1366 NULL,
1367 MHD_OPTION_CONNECTION_TIMEOUT,
1368 (unsigned int) 16,
1369 MHD_OPTION_NOTIFY_CONNECTION,
1370 &mhd_connection_cb,
1371 NULL,
1372 MHD_OPTION_URI_LOG_CALLBACK,
1373 mhd_log_callback,
1374 NULL,
1375 MHD_OPTION_NOTIFY_COMPLETED,
1376 &mhd_completed_cb,
1377 NULL,
1378 MHD_OPTION_END);
1379 if (NULL == httpd)
1380 {
1381 GNUNET_SCHEDULER_shutdown ();
1382 return;
1383 }
1384 /* Load plugins */
1385 // FIXME: Use per-plugin rest plugin structs
1386 struct GNUNET_REST_Plugin *config_plugin = REST_config_init (cfg);
1387 if (GNUNET_OK != setup_plugin (config_plugin->name,
1388 &REST_config_process_request, config_plugin))
1389 {
1390 GNUNET_SCHEDULER_shutdown ();
1391 }
1392 struct GNUNET_REST_Plugin *copying_plugin = REST_copying_init (cfg);
1393 if (GNUNET_OK != setup_plugin (copying_plugin->name,
1394 &REST_copying_process_request, copying_plugin))
1395 {
1396 GNUNET_SCHEDULER_shutdown ();
1397 }
1398 struct GNUNET_REST_Plugin *identity_plugin = REST_identity_init (cfg);
1399 if (GNUNET_OK != setup_plugin (identity_plugin->name,
1400 &REST_identity_process_request,
1401 identity_plugin))
1402 {
1403 GNUNET_SCHEDULER_shutdown ();
1404 }
1405 struct GNUNET_REST_Plugin *namestore_plugin = REST_namestore_init (cfg);
1406 if (GNUNET_OK != setup_plugin (namestore_plugin->name,
1407 &REST_namestore_process_request,
1408 namestore_plugin))
1409 {
1410 GNUNET_SCHEDULER_shutdown ();
1411 }
1412 struct GNUNET_REST_Plugin *gns_plugin = REST_gns_init (cfg);
1413 if (GNUNET_OK != setup_plugin (gns_plugin->name, &REST_gns_process_request,
1414 gns_plugin))
1415 {
1416 GNUNET_SCHEDULER_shutdown ();
1417 }
1418#if HAVE_JOSE
1419 struct GNUNET_REST_Plugin *openid_plugin = REST_openid_init (cfg);
1420 if (GNUNET_OK != setup_plugin (openid_plugin->name,
1421 &REST_openid_process_request, openid_plugin))
1422 {
1423 GNUNET_SCHEDULER_shutdown ();
1424 }
1425#endif
1426 struct GNUNET_REST_Plugin *reclaim_plugin = REST_reclaim_init (cfg);
1427 if (GNUNET_OK != setup_plugin (reclaim_plugin->name,
1428 &REST_reclaim_process_request, reclaim_plugin))
1429 {
1430 GNUNET_SCHEDULER_shutdown ();
1431 }
1432 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1433}
1434
1435
1436GNUNET_DAEMON_MAIN ("rest", _ ("GNUnet REST service"), &run)
1437
1438/* end of gnunet-rest-server.c */