error handling
[oweals/gnunet.git] / src / rest / gnunet-rest-server.c
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
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  */
65 static struct GNUNET_SCHEDULER_Task *httpd_task;
66
67 /**
68  * The address to bind to
69  */
70 static in_addr_t address;
71
72 /**
73  * The IPv6 address to bind to
74  */
75 static struct in6_addr address6;
76
77 /**
78  * The port the service is running on (default 7776)
79  */
80 static unsigned long long port = GNUNET_REST_SERVICE_PORT;
81
82 /**
83  * The listen socket of the service for IPv4
84  */
85 static struct GNUNET_NETWORK_Handle *lsock4;
86
87 /**
88  * The listen socket of the service for IPv6
89  */
90 static struct GNUNET_NETWORK_Handle *lsock6;
91
92 /**
93  * The listen task ID for IPv4
94  */
95 static struct GNUNET_SCHEDULER_Task *ltask4;
96
97 /**
98  * The listen task ID for IPv6
99  */
100 static struct GNUNET_SCHEDULER_Task *ltask6;
101
102 /**
103  * Daemon for HTTP
104  */
105 static struct MHD_Daemon *httpd;
106
107 /**
108  * Response we return on failures.
109  */
110 static struct MHD_Response *failure_response;
111
112 /**
113  * Our configuration.
114  */
115 static const struct GNUNET_CONFIGURATION_Handle *cfg;
116
117 /**
118  * Map of loaded plugins.
119  */
120 static struct GNUNET_CONTAINER_MultiHashMap *plugin_map;
121
122 /**
123  * Echo request Origin in CORS
124  */
125 static int echo_origin;
126
127 /**
128  * Allowed Origins (CORS)
129  */
130 static char *allow_origins;
131
132 /**
133  * Allowed Headers (CORS)
134  */
135 static char *allow_headers;
136
137 /**
138  * Allowed Credentials (CORS)
139  */
140 static char *allow_credentials;
141
142 /**
143  * MHD Connection handle
144  */
145 struct MhdConnectionHandle
146 {
147   struct MHD_Connection *con;
148
149   struct MHD_Response *response;
150
151   struct GNUNET_REST_Plugin *plugin;
152
153   struct GNUNET_REST_RequestHandle *data_handle;
154
155   struct MHD_PostProcessor *pp;
156
157   int status;
158
159   int state;
160 };
161
162 /* ************************* Global helpers ********************* */
163
164
165 /**
166  * Task run whenever HTTP server operations are pending.
167  *
168  * @param cls NULL
169  */
170 static void
171 do_httpd (void *cls);
172
173
174 /**
175  * Run MHD now, we have extra data ready for the callback.
176  */
177 static void
178 run_mhd_now ()
179 {
180   if (NULL != httpd_task)
181   {
182     GNUNET_SCHEDULER_cancel (httpd_task);
183     httpd_task = NULL;
184   }
185   httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
186 }
187
188
189 /**
190  * Plugin result callback
191  *
192  * @param cls closure (MHD connection handle)
193  * @param data the data to return to the caller
194  * @param len length of the data
195  * @param status #GNUNET_OK if successful
196  */
197 static void
198 plugin_callback (void *cls, struct MHD_Response *resp, int status)
199 {
200   struct MhdConnectionHandle *handle = cls;
201
202   handle->status = status;
203   handle->response = resp;
204   MHD_resume_connection (handle->con);
205   run_mhd_now ();
206 }
207
208
209 static int
210 cleanup_url_map (void *cls, const struct GNUNET_HashCode *key, void *value)
211 {
212   GNUNET_free_non_null (value);
213   return GNUNET_YES;
214 }
215
216
217 static void
218 cleanup_handle (struct MhdConnectionHandle *handle)
219 {
220   if (NULL != handle->response)
221     MHD_destroy_response (handle->response);
222   if (NULL != handle->data_handle)
223   {
224     if (NULL != handle->data_handle->header_param_map)
225     {
226       GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle
227                                              ->header_param_map,
228                                              &cleanup_url_map,
229                                              NULL);
230       GNUNET_CONTAINER_multihashmap_destroy (
231         handle->data_handle->header_param_map);
232     }
233     if (NULL != handle->data_handle->url_param_map)
234     {
235       GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle->url_param_map,
236                                              &cleanup_url_map,
237                                              NULL);
238       GNUNET_CONTAINER_multihashmap_destroy (
239         handle->data_handle->url_param_map);
240     }
241     GNUNET_free (handle->data_handle);
242   }
243   GNUNET_free (handle);
244 }
245
246
247 static int
248 header_iterator (void *cls,
249                  enum MHD_ValueKind kind,
250                  const char *key,
251                  const char *value)
252 {
253   struct GNUNET_REST_RequestHandle *handle = cls;
254   struct GNUNET_HashCode hkey;
255   char *val;
256   char *lowerkey;
257
258   lowerkey = GNUNET_strdup (key);
259   GNUNET_STRINGS_utf8_tolower (key, lowerkey);
260   GNUNET_CRYPTO_hash (lowerkey, strlen (lowerkey), &hkey);
261   GNUNET_asprintf (&val, "%s", value);
262   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
263         handle->header_param_map,
264         &hkey,
265         val,
266         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
267   {
268     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
269                 "Could not load add header `%s'=%s\n",
270                 lowerkey,
271                 value);
272   }
273   GNUNET_free (lowerkey);
274   return MHD_YES;
275 }
276
277
278 static int
279 url_iterator (void *cls,
280               enum MHD_ValueKind kind,
281               const char *key,
282               const char *value)
283 {
284   struct GNUNET_REST_RequestHandle *handle = cls;
285   struct GNUNET_HashCode hkey;
286   char *val;
287
288   GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
289   GNUNET_asprintf (&val, "%s", value);
290   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
291         handle->url_param_map,
292         &hkey,
293         val,
294         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
295   {
296     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
297                 "Could not load add url param `%s'=%s\n",
298                 key,
299                 value);
300   }
301   return MHD_YES;
302 }
303
304
305 static int
306 post_data_iter (void *cls,
307                 enum MHD_ValueKind kind,
308                 const char *key,
309                 const char *filename,
310                 const char *content_type,
311                 const char *transfer_encoding,
312                 const char *data,
313                 uint64_t off,
314                 size_t size)
315 {
316   struct GNUNET_REST_RequestHandle *handle = cls;
317   struct GNUNET_HashCode hkey;
318   char *val;
319
320   if (MHD_POSTDATA_KIND != kind)
321     return MHD_YES;
322
323   GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
324   GNUNET_asprintf (&val, "%s", data);
325   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
326         handle->url_param_map,
327         &hkey,
328         val,
329         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
330   {
331     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
332                 "Could not load add url param '%s'=%s\n",
333                 key,
334                 data);
335     GNUNET_free (val);
336   }
337   return MHD_YES;
338 }
339
340
341 /* ********************************* MHD response generation ******************* */
342
343 /**
344  * Main MHD callback for handling requests.
345  *
346  * @param cls unused
347  * @param con MHD connection handle
348  * @param url the url in the request
349  * @param meth the HTTP method used ("GET", "PUT", etc.)
350  * @param ver the HTTP version string (i.e. "HTTP/1.1")
351  * @param upload_data the data being uploaded (excluding HEADERS,
352  *        for a POST that fits into memory and that is encoded
353  *        with a supported encoding, the POST data will NOT be
354  *        given in upload_data and is instead available as
355  *        part of MHD_get_connection_values; very large POST
356  *        data *will* be made available incrementally in
357  *        upload_data)
358  * @param upload_data_size set initially to the size of the
359  *        @a upload_data provided; the method must update this
360  *        value to the number of bytes NOT processed;
361  * @param con_cls pointer to location where we store the 'struct Request'
362  * @return MHD_YES if the connection was handled successfully,
363  *         MHD_NO if the socket must be closed due to a serious
364  *         error while handling the request
365  */
366 static int
367 create_response (void *cls,
368                  struct MHD_Connection *con,
369                  const char *url,
370                  const char *meth,
371                  const char *ver,
372                  const char *upload_data,
373                  size_t *upload_data_size,
374                  void **con_cls)
375 {
376   char *plugin_name;
377   char *origin;
378   struct GNUNET_HashCode key;
379   struct MhdConnectionHandle *con_handle;
380   struct GNUNET_REST_RequestHandle *rest_conndata_handle;
381
382   con_handle = *con_cls;
383
384   if (NULL == *con_cls)
385   {
386     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New connection %s\n", url);
387     char tmp_url[strlen (url) + 1];
388     strcpy (tmp_url, url);
389     con_handle = GNUNET_new (struct MhdConnectionHandle);
390     con_handle->con = con;
391     con_handle->state = GN_REST_STATE_INIT;
392     *con_cls = con_handle;
393
394     plugin_name = strtok (tmp_url, "/");
395
396     if (NULL != plugin_name)
397     {
398       GNUNET_CRYPTO_hash (plugin_name, strlen (plugin_name), &key);
399
400       con_handle->plugin = GNUNET_CONTAINER_multihashmap_get (plugin_map, &key);
401     }
402     if (NULL == con_handle->plugin)
403     {
404       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Queueing response with MHD\n");
405       GNUNET_free (con_handle);
406       return MHD_queue_response (con, MHD_HTTP_NOT_FOUND, failure_response);
407     }
408
409     return MHD_YES;
410   }
411   if (GN_REST_STATE_INIT == con_handle->state)
412   {
413     rest_conndata_handle = GNUNET_new (struct GNUNET_REST_RequestHandle);
414     rest_conndata_handle->method = meth;
415     rest_conndata_handle->url = url;
416     rest_conndata_handle->data = upload_data;
417     rest_conndata_handle->data_size = *upload_data_size;
418     rest_conndata_handle->url_param_map =
419       GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
420     rest_conndata_handle->header_param_map =
421       GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
422     con_handle->data_handle = rest_conndata_handle;
423     MHD_get_connection_values (con,
424                                MHD_GET_ARGUMENT_KIND,
425                                (MHD_KeyValueIterator) & url_iterator,
426                                rest_conndata_handle);
427     MHD_get_connection_values (con,
428                                MHD_HEADER_KIND,
429                                (MHD_KeyValueIterator) & header_iterator,
430                                rest_conndata_handle);
431     con_handle->pp = MHD_create_post_processor (con,
432                                                 65536,
433                                                 post_data_iter,
434                                                 rest_conndata_handle);
435     if (*upload_data_size)
436     {
437       MHD_post_process (con_handle->pp, upload_data, *upload_data_size);
438     }
439     MHD_destroy_post_processor (con_handle->pp);
440
441     con_handle->state = GN_REST_STATE_PROCESSING;
442     con_handle->plugin->process_request (rest_conndata_handle,
443                                          &plugin_callback,
444                                          con_handle);
445     *upload_data_size = 0;
446     run_mhd_now ();
447     return MHD_YES;
448   }
449   if (NULL == con_handle->response)
450   {
451     // Suspend connection until plugin is done
452     MHD_suspend_connection (con_handle->con);
453     return MHD_YES;
454   }
455   MHD_resume_connection (con_handle->con);
456   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
457               "Queueing response from plugin with MHD\n");
458   // Handle Preflights for extensions
459   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking origin\n");
460   GNUNET_CRYPTO_hash ("origin", strlen ("origin"), &key);
461   origin = GNUNET_CONTAINER_multihashmap_get (con_handle->data_handle
462                                               ->header_param_map,
463                                               &key);
464   if (NULL != origin)
465   {
466     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Origin: %s\n", origin);
467     // Only echo for browser plugins
468     if (GNUNET_YES == echo_origin)
469     {
470       if ((0 ==
471            strncmp ("moz-extension://", origin, strlen ("moz-extension://"))) ||
472           (0 == strncmp ("chrome-extension://",
473                          origin,
474                          strlen ("chrome-extension://"))))
475       {
476         MHD_add_response_header (con_handle->response,
477                                  MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
478                                  origin);
479       }
480     }
481     if (NULL != allow_origins)
482     {
483       char *tmp = GNUNET_strdup (allow_origins);
484       char *allow_origin = strtok (tmp, ",");
485       while (NULL != allow_origin)
486       {
487         if (0 == strncmp (allow_origin, origin, strlen (allow_origin)))
488         {
489           MHD_add_response_header (con_handle->response,
490                                    MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
491                                    allow_origin);
492           break;
493         }
494         allow_origin = strtok (NULL, ",");
495       }
496       GNUNET_free (tmp);
497     }
498   }
499   if (NULL != allow_credentials)
500   {
501     MHD_add_response_header (con_handle->response,
502                              "Access-Control-Allow-Credentials",
503                              allow_credentials);
504   }
505   if (NULL != allow_headers)
506   {
507     MHD_add_response_header (con_handle->response,
508                              "Access-Control-Allow-Headers",
509                              allow_headers);
510   }
511   run_mhd_now ();
512   int ret = MHD_queue_response (con, con_handle->status, con_handle->response);
513   cleanup_handle (con_handle);
514   return ret;
515 }
516
517
518 /* ******************** MHD HTTP setup and event loop ******************** */
519
520 /**
521  * Function called when MHD decides that we are done with a connection.
522  *
523  * @param cls NULL
524  * @param connection connection handle
525  * @param con_cls value as set by the last call to
526  *        the MHD_AccessHandlerCallback, should be our handle
527  * @param toe reason for request termination (ignored)
528  */
529 static void
530 mhd_completed_cb (void *cls,
531                   struct MHD_Connection *connection,
532                   void **con_cls,
533                   enum MHD_RequestTerminationCode toe)
534 {
535   if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
536     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
537                 "MHD encountered error handling request: %d\n",
538                 toe);
539 }
540
541
542 /**
543  * Kill the MHD daemon.
544  */
545 static void
546 kill_httpd ()
547 {
548   if (NULL != httpd)
549   {
550     MHD_stop_daemon (httpd);
551     httpd = NULL;
552   }
553   if (NULL != httpd_task)
554   {
555     GNUNET_SCHEDULER_cancel (httpd_task);
556     httpd_task = NULL;
557   }
558   if (NULL != ltask4)
559   {
560     GNUNET_SCHEDULER_cancel (ltask4);
561     ltask4 = NULL;
562   }
563   if (NULL != ltask6)
564   {
565     GNUNET_SCHEDULER_cancel (ltask6);
566     ltask6 = NULL;
567   }
568
569   if (NULL != lsock4)
570   {
571     GNUNET_NETWORK_socket_close (lsock4);
572     lsock4 = NULL;
573   }
574   if (NULL != lsock6)
575   {
576     GNUNET_NETWORK_socket_close (lsock6);
577     lsock6 = NULL;
578   }
579 }
580
581
582 /**
583  * Schedule MHD.  This function should be called initially when an
584  * MHD is first getting its client socket, and will then automatically
585  * always be called later whenever there is work to be done.
586  *
587  * @param hd the daemon to schedule
588  */
589 static void
590 schedule_httpd ()
591 {
592   fd_set rs;
593   fd_set ws;
594   fd_set es;
595   struct GNUNET_NETWORK_FDSet *wrs;
596   struct GNUNET_NETWORK_FDSet *wws;
597   int max;
598   int haveto;
599   MHD_UNSIGNED_LONG_LONG timeout;
600   struct GNUNET_TIME_Relative tv;
601
602   FD_ZERO (&rs);
603   FD_ZERO (&ws);
604   FD_ZERO (&es);
605   max = -1;
606   if (MHD_YES != MHD_get_fdset (httpd, &rs, &ws, &es, &max))
607   {
608     kill_httpd ();
609     return;
610   }
611   haveto = MHD_get_timeout (httpd, &timeout);
612   if (MHD_YES == haveto)
613     tv.rel_value_us = (uint64_t) timeout * 1000LL;
614   else
615     tv = GNUNET_TIME_UNIT_FOREVER_REL;
616   if (-1 != max)
617   {
618     wrs = GNUNET_NETWORK_fdset_create ();
619     wws = GNUNET_NETWORK_fdset_create ();
620     GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
621     GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
622   }
623   else
624   {
625     wrs = NULL;
626     wws = NULL;
627   }
628   if (NULL != httpd_task)
629   {
630     GNUNET_SCHEDULER_cancel (httpd_task);
631     httpd_task = NULL;
632   }
633   if ((MHD_YES == haveto) || (-1 != max))
634   {
635     httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
636                                               tv,
637                                               wrs,
638                                               wws,
639                                               &do_httpd,
640                                               NULL);
641   }
642   if (NULL != wrs)
643     GNUNET_NETWORK_fdset_destroy (wrs);
644   if (NULL != wws)
645     GNUNET_NETWORK_fdset_destroy (wws);
646 }
647
648
649 /**
650  * Task run whenever HTTP server operations are pending.
651  *
652  * @param cls NULL
653  */
654 static void
655 do_httpd (void *cls)
656 {
657   httpd_task = NULL;
658   MHD_run (httpd);
659   schedule_httpd ();
660 }
661
662
663 /**
664  * Accept new incoming connections
665  *
666  * @param cls the closure with the lsock4 or lsock6
667  * @param tc the scheduler context
668  */
669 static void
670 do_accept (void *cls)
671 {
672   struct GNUNET_NETWORK_Handle *lsock = cls;
673   struct GNUNET_NETWORK_Handle *s;
674   int fd;
675   const struct sockaddr *addr;
676   socklen_t len;
677
678   GNUNET_assert (NULL != lsock);
679   if (lsock == lsock4)
680   {
681     ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
682                                             lsock,
683                                             &do_accept,
684                                             lsock);
685   }
686   else if (lsock == lsock6)
687   {
688     ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
689                                             lsock,
690                                             &do_accept,
691                                             lsock);
692   }
693   else
694     GNUNET_assert (0);
695   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
696   if (NULL == s)
697   {
698     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
699     return;
700   }
701   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
702               "Got an inbound connection, waiting for data\n");
703   fd = GNUNET_NETWORK_get_fd (s);
704   addr = GNUNET_NETWORK_get_addr (s);
705   len = GNUNET_NETWORK_get_addrlen (s);
706   if (MHD_YES != MHD_add_connection (httpd, fd, addr, len))
707   {
708     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
709                 _ ("Failed to pass client to MHD\n"));
710     return;
711   }
712   GNUNET_free (s);
713   schedule_httpd ();
714 }
715
716
717 /**
718  * Task run on shutdown
719  *
720  * @param cls closure
721  */
722 static void
723 do_shutdown (void *cls)
724 {
725   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down...\n");
726   kill_httpd ();
727   GNUNET_free_non_null (allow_credentials);
728   GNUNET_free_non_null (allow_headers);
729 }
730
731
732 /**
733  * Create an IPv4 listen socket bound to our port.
734  *
735  * @return NULL on error
736  */
737 static struct GNUNET_NETWORK_Handle *
738 bind_v4 ()
739 {
740   struct GNUNET_NETWORK_Handle *ls;
741   struct sockaddr_in sa4;
742   int eno;
743
744   memset (&sa4, 0, sizeof(sa4));
745   sa4.sin_family = AF_INET;
746   sa4.sin_port = htons (port);
747   sa4.sin_addr.s_addr = address;
748 #if HAVE_SOCKADDR_IN_SIN_LEN
749   sa4.sin_len = sizeof(sa4);
750 #endif
751   ls = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
752   if (NULL == ls)
753     return NULL;
754   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls,
755                                                (const struct sockaddr *) &sa4,
756                                                sizeof(sa4)))
757   {
758     eno = errno;
759     GNUNET_NETWORK_socket_close (ls);
760     errno = eno;
761     return NULL;
762   }
763   return ls;
764 }
765
766
767 /**
768  * Create an IPv6 listen socket bound to our port.
769  *
770  * @return NULL on error
771  */
772 static struct GNUNET_NETWORK_Handle *
773 bind_v6 ()
774 {
775   struct GNUNET_NETWORK_Handle *ls;
776   struct sockaddr_in6 sa6;
777   int eno;
778
779   memset (&sa6, 0, sizeof(sa6));
780   sa6.sin6_family = AF_INET6;
781   sa6.sin6_port = htons (port);
782   sa6.sin6_addr = address6;
783 #if HAVE_SOCKADDR_IN_SIN_LEN
784   sa6.sin6_len = sizeof(sa6);
785 #endif
786   ls = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_STREAM, 0);
787   if (NULL == ls)
788     return NULL;
789   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls,
790                                                (const struct sockaddr *) &sa6,
791                                                sizeof(sa6)))
792   {
793     eno = errno;
794     GNUNET_NETWORK_socket_close (ls);
795     errno = eno;
796     return NULL;
797   }
798   return ls;
799 }
800
801
802 /**
803  * Callback for plugin load
804  *
805  * @param cls NULL
806  * @param libname the name of the library loaded
807  * @param lib_ret the object returned by the plugin initializer
808  */
809 static void
810 load_plugin (void *cls, const char *libname, void *lib_ret)
811 {
812   struct GNUNET_REST_Plugin *plugin = lib_ret;
813   struct GNUNET_HashCode key;
814
815   if (NULL == lib_ret)
816   {
817     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
818                 "Could not load plugin `%s'\n",
819                 libname);
820     return;
821   }
822   GNUNET_assert (1 < strlen (plugin->name));
823   GNUNET_assert ('/' == *plugin->name);
824   GNUNET_CRYPTO_hash (plugin->name + 1, strlen (plugin->name + 1), &key);
825   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
826         plugin_map,
827         &key,
828         plugin,
829         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
830   {
831     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
832                 "Could not load add plugin `%s'\n",
833                 libname);
834     return;
835   }
836   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded plugin `%s'\n", libname);
837 }
838
839
840 /**
841  * Main function that will be run
842  *
843  * @param cls closure
844  * @param args remaining command-line arguments
845  * @param cfgfile name of the configuration file used (for saving, can be NULL)
846  * @param c configuration
847  */
848 static void
849 run (void *cls,
850      char *const *args,
851      const char *cfgfile,
852      const struct GNUNET_CONFIGURATION_Handle *c)
853 {
854   char *addr_str;
855
856   cfg = c;
857   plugin_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
858
859   /* Get port to bind to */
860   if (GNUNET_OK !=
861       GNUNET_CONFIGURATION_get_value_number (cfg, "rest", "HTTP_PORT", &port))
862   {
863     // No address specified
864     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Using default port...\n");
865     port = GNUNET_REST_SERVICE_PORT;
866   }
867
868   /* Get address to bind to */
869   if (GNUNET_OK !=
870       GNUNET_CONFIGURATION_get_value_string (cfg, "rest", "BIND_TO", &addr_str))
871   {
872     // No address specified
873     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Don't know what to bind to...\n");
874     GNUNET_SCHEDULER_shutdown ();
875     return;
876   }
877   if (1 != inet_pton (AF_INET, addr_str, &address))
878   {
879     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
880                 "Unable to parse address %s\n",
881                 addr_str);
882     GNUNET_free (addr_str);
883     GNUNET_SCHEDULER_shutdown ();
884     return;
885   }
886   GNUNET_free (addr_str);
887   /* Get address to bind to */
888   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
889                                                           "rest",
890                                                           "BIND_TO6",
891                                                           &addr_str))
892   {
893     // No address specified
894     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Don't know what to bind6 to...\n");
895     GNUNET_SCHEDULER_shutdown ();
896     return;
897   }
898   if (1 != inet_pton (AF_INET6, addr_str, &address6))
899   {
900     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
901                 "Unable to parse IPv6 address %s\n",
902                 addr_str);
903     GNUNET_free (addr_str);
904     GNUNET_SCHEDULER_shutdown ();
905     return;
906   }
907   GNUNET_free (addr_str);
908
909
910   /* Get CORS data from cfg */
911   echo_origin =
912     GNUNET_CONFIGURATION_get_value_yesno (cfg,
913                                           "rest",
914                                           "REST_ECHO_ORIGIN_WEBEXT");
915   allow_origins = NULL;
916   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
917                                                           "rest",
918                                                           "REST_ALLOW_ORIGIN",
919                                                           &allow_origins))
920   {
921     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
922                 "No CORS Access-Control-Allow-Origin header will be sent...\n");
923   }
924   if (GNUNET_OK !=
925       GNUNET_CONFIGURATION_get_value_string (cfg,
926                                              "rest",
927                                              "REST_ALLOW_CREDENTIALS",
928                                              &allow_credentials))
929   {
930     // No origin specified
931     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
932                 "No CORS Credential Header will be sent...\n");
933   }
934
935   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
936                                                           "rest",
937                                                           "REST_ALLOW_HEADERS",
938                                                           &allow_headers))
939   {
940     // No origin specified
941     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
942                 "No CORS Access-Control-Allow-Headers Header will be sent...\n");
943   }
944
945   /* Open listen socket proxy */
946   lsock6 = bind_v6 ();
947   if (NULL == lsock6)
948   {
949     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
950   }
951   else
952   {
953     if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
954     {
955       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
956       GNUNET_NETWORK_socket_close (lsock6);
957       lsock6 = NULL;
958     }
959     else
960     {
961       ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
962                                               lsock6,
963                                               &do_accept,
964                                               lsock6);
965     }
966   }
967   lsock4 = bind_v4 ();
968   if (NULL == lsock4)
969   {
970     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
971   }
972   else
973   {
974     if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
975     {
976       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
977       GNUNET_NETWORK_socket_close (lsock4);
978       lsock4 = NULL;
979     }
980     else
981     {
982       ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
983                                               lsock4,
984                                               &do_accept,
985                                               lsock4);
986     }
987   }
988   if ((NULL == lsock4) && (NULL == lsock6))
989   {
990     GNUNET_SCHEDULER_shutdown ();
991     return;
992   }
993   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service listens on port %llu\n", port);
994   httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET
995                             | MHD_ALLOW_SUSPEND_RESUME,
996                             0,
997                             NULL,
998                             NULL,
999                             &create_response,
1000                             NULL,
1001                             MHD_OPTION_CONNECTION_TIMEOUT,
1002                             (unsigned int) 16,
1003                             MHD_OPTION_NOTIFY_COMPLETED,
1004                             &mhd_completed_cb,
1005                             NULL,
1006                             MHD_OPTION_END);
1007   if (NULL == httpd)
1008   {
1009     GNUNET_SCHEDULER_shutdown ();
1010     return;
1011   }
1012   /* Load plugins */
1013   GNUNET_PLUGIN_load_all ("libgnunet_plugin_rest",
1014                           (void *) cfg,
1015                           &load_plugin,
1016                           NULL);
1017   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1018 }
1019
1020
1021 /**
1022  *
1023  * The main function for gnunet-rest-service
1024  *
1025  * @param argc number of arguments from the cli
1026  * @param argv command line arguments
1027  * @return 0 ok, 1 on error
1028  *
1029  */
1030 int
1031 main (int argc, char *const *argv)
1032 {
1033   struct GNUNET_GETOPT_CommandLineOption options[] =
1034   { GNUNET_GETOPT_OPTION_END };
1035   static const char *err_page = "{}";
1036   int ret;
1037
1038   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1039     return 2;
1040   GNUNET_log_setup ("gnunet-rest-server", "WARNING", NULL);
1041   failure_response = MHD_create_response_from_buffer (strlen (err_page),
1042                                                       (void *) err_page,
1043                                                       MHD_RESPMEM_PERSISTENT);
1044   ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc,
1045                                           argv,
1046                                           "gnunet-rest-server",
1047                                           _ ("GNUnet REST server"),
1048                                           options,
1049                                           &run,
1050                                           NULL))
1051         ? 0
1052         : 1;
1053   MHD_destroy_response (failure_response);
1054   GNUNET_free_non_null ((char *) argv);
1055   return ret;
1056 }
1057
1058
1059 /* end of gnunet-rest-server.c */