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