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