2260cb74e047281479e1b28ef1ad03fb9960edd0
[oweals/gnunet.git] / src / transport / plugin_transport_http_server.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/plugin_transport_http.c
23  * @brief http transport service plugin
24  * @author Matthias Wachs
25  */
26
27 #include "plugin_transport_http.h"
28
29 static void
30 server_log (void *arg, const char *fmt, va_list ap)
31 {
32   char text[1024];
33
34   vsnprintf (text, sizeof (text), fmt, ap);
35   va_end (ap);
36   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "server: %s\n", text);
37 }
38
39 /**
40  * Check if incoming connection is accepted.
41  * NOTE: Here every connection is accepted
42  * @param cls plugin as closure
43  * @param addr address of incoming connection
44  * @param addr_len address length of incoming connection
45  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
46  *
47  */
48 static int
49 server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
50 {
51   struct Plugin * plugin = cls;
52   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "server: server_accept_cb\n");
53   if (plugin->cur_connections <= plugin->max_connections)
54     return MHD_YES;
55   else
56     return MHD_NO;
57 }
58
59
60 /**
61  * Callback called by MHD when it needs data to send
62  * @param cls current session
63  * @param pos position in buffer
64  * @param buf the buffer to write data to
65  * @param max max number of bytes available in buffer
66  * @return bytes written to buffer
67  */
68 #if 0
69 static ssize_t
70 server_send_cb (void *cls, uint64_t pos, char *buf, size_t max)
71 {
72
73   return 0;
74 }
75 #endif
76
77
78 #if BUILD_HTTPS
79 static char *
80 server_load_file (const char *file)
81 {
82   struct GNUNET_DISK_FileHandle *gn_file;
83   struct stat fstat;
84   char *text = NULL;
85
86   if (0 != STAT (file, &fstat))
87     return NULL;
88   text = GNUNET_malloc (fstat.st_size + 1);
89   gn_file =
90       GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
91                              GNUNET_DISK_PERM_USER_READ);
92   if (gn_file == NULL)
93   {
94     GNUNET_free (text);
95     return NULL;
96   }
97   if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fstat.st_size))
98   {
99     GNUNET_free (text);
100     GNUNET_DISK_file_close (gn_file);
101     return NULL;
102   }
103   text[fstat.st_size] = '\0';
104   GNUNET_DISK_file_close (gn_file);
105   return text;
106 }
107 #endif
108
109
110 #if BUILD_HTTPS
111
112 static int
113 server_load_certificate (struct Plugin *plugin)
114 {
115   int res = GNUNET_OK;
116
117   char *key_file;
118   char *cert_file;
119
120   /* Get crypto init string from config
121    * If not present just use default values */
122   GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
123                                          "CRYPTO_INIT", &plugin->crypto_init);
124
125   if (GNUNET_OK !=
126       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
127                                                "KEY_FILE", &key_file))
128   {
129     key_file = "https_key.key";
130   }
131
132   if (GNUNET_OK !=
133       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
134                                                "CERT_FILE", &cert_file))
135   {
136     cert_file = "https_cert.crt";
137   }
138
139   /* read key & certificates from file */
140 #if VERBOSE_SERVER
141   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
142               "Loading TLS certificate from key-file `%s' cert-file`%s'\n",
143               key_file, cert_file);
144 #endif
145
146   plugin->key = server_load_file (key_file);
147   plugin->cert = server_load_file (cert_file);
148
149   if ((plugin->key == NULL) || (plugin->cert == NULL))
150   {
151     struct GNUNET_OS_Process *cert_creation;
152
153     GNUNET_free_non_null (plugin->key);
154     plugin->key = NULL;
155     GNUNET_free_non_null (plugin->cert);
156     plugin->cert = NULL;
157
158 #if VERBOSE_SERVER
159     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
160                 "No usable TLS certificate found, creating certificate\n");
161 #endif
162     errno = 0;
163     cert_creation =
164         GNUNET_OS_start_process (NULL, NULL,
165                                  "gnunet-transport-certificate-creation",
166                                  "gnunet-transport-certificate-creation",
167                                  key_file, cert_file, NULL);
168     if (cert_creation == NULL)
169     {
170       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
171                        _
172                        ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
173       GNUNET_free (key_file);
174       GNUNET_free (cert_file);
175
176       GNUNET_free_non_null (plugin->key);
177       GNUNET_free_non_null (plugin->cert);
178       GNUNET_free_non_null (plugin->crypto_init);
179
180       return GNUNET_SYSERR;
181     }
182     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation));
183     GNUNET_OS_process_close (cert_creation);
184
185     plugin->key = server_load_file (key_file);
186     plugin->cert = server_load_file (cert_file);
187   }
188
189   if ((plugin->key == NULL) || (plugin->cert == NULL))
190   {
191     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
192                      _
193                      ("No usable TLS certificate found and creating one failed!\n"),
194                      "transport-https");
195     GNUNET_free (key_file);
196     GNUNET_free (cert_file);
197
198     GNUNET_free_non_null (plugin->key);
199     GNUNET_free_non_null (plugin->cert);
200     GNUNET_free_non_null (plugin->crypto_init);
201
202     return GNUNET_SYSERR;
203   }
204   GNUNET_free (key_file);
205   GNUNET_free (cert_file);
206 #if DEBUG_HTTP
207   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
208 #endif
209
210   return res;
211 }
212 #endif
213
214
215 /**
216  * Process GET or PUT request received via MHD.  For
217  * GET, queue response that will send back our pending
218  * messages.  For PUT, process incoming data and send
219  * to GNUnet core.  In either case, check if a session
220  * already exists and create a new one if not.
221  */
222 static int
223 server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
224                   const char *url, const char *method, const char *version,
225                   const char *upload_data, size_t * upload_data_size,
226                   void **httpSessionCache)
227 {
228   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "server: server_access_cb\n");
229   return 0;
230 }
231
232 static void
233 server_disconnect_cb (void *cls, struct MHD_Connection *connection,
234                       void **httpSessionCache)
235 {
236   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "server: server_disconnect_cb\n");
237 }
238
239 int
240 server_disconnect (struct Session *s)
241 {
242   return GNUNET_OK;
243 }
244
245 int
246 server_send (struct Session *s, const char *msgbuf, size_t msgbuf_size)
247 {
248   return GNUNET_OK;
249 }
250
251 /**
252  * Function that queries MHD's select sets and
253  * starts the task waiting for them.
254  * @param plugin plugin
255  * @param daemon_handle the MHD daemon handle
256  * @return gnunet task identifier
257  */
258 static GNUNET_SCHEDULER_TaskIdentifier
259 server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle);
260
261 /**
262  * Call MHD IPv4 to process pending requests and then go back
263  * and schedule the next run.
264  * @param cls plugin as closure
265  * @param tc task context
266  */
267 static void
268 server_v4_run (void *cls,
269                            const struct GNUNET_SCHEDULER_TaskContext *tc)
270 {
271   struct Plugin *plugin = cls;
272   GNUNET_assert (cls != NULL);
273
274   plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
275
276   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
277     return;
278
279   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4));
280   plugin->server_v4_task = server_schedule (plugin, plugin->server_v4);
281 }
282
283
284 /**
285  * Call MHD IPv6 to process pending requests and then go back
286  * and schedule the next run.
287  * @param cls plugin as closure
288  * @param tc task context
289  */
290 static void
291 server_v6_run (void *cls,
292                            const struct GNUNET_SCHEDULER_TaskContext *tc)
293 {
294   struct Plugin *plugin = cls;
295   GNUNET_assert (cls != NULL);
296
297   plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
298
299   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
300     return;
301
302   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6));
303   plugin->server_v6_task = server_schedule (plugin, plugin->server_v6);
304 }
305
306 /**
307  * Function that queries MHD's select sets and
308  * starts the task waiting for them.
309  * @param plugin plugin
310  * @param daemon_handle the MHD daemon handle
311  * @return gnunet task identifier
312  */
313 static GNUNET_SCHEDULER_TaskIdentifier
314 server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle)
315 {
316   GNUNET_SCHEDULER_TaskIdentifier ret;
317   fd_set rs;
318   fd_set ws;
319   fd_set es;
320   struct GNUNET_NETWORK_FDSet *wrs;
321   struct GNUNET_NETWORK_FDSet *wws;
322   struct GNUNET_NETWORK_FDSet *wes;
323   int max;
324   unsigned long long timeout;
325   int haveto;
326   struct GNUNET_TIME_Relative tv;
327
328   ret = GNUNET_SCHEDULER_NO_TASK;
329   FD_ZERO (&rs);
330   FD_ZERO (&ws);
331   FD_ZERO (&es);
332   wrs = GNUNET_NETWORK_fdset_create ();
333   wes = GNUNET_NETWORK_fdset_create ();
334   wws = GNUNET_NETWORK_fdset_create ();
335   max = -1;
336   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
337   haveto = MHD_get_timeout (daemon_handle, &timeout);
338   if (haveto == MHD_YES)
339     tv.rel_value = (uint64_t) timeout;
340   else
341     tv = GNUNET_TIME_UNIT_SECONDS;
342   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
343   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
344   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
345   if (daemon_handle == plugin->server_v4)
346   {
347     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
348     {
349       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
350       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
351     }
352
353     ret =
354         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
355                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
356                                      &server_v4_run, plugin);
357   }
358   if (daemon_handle == plugin->server_v6)
359   {
360     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
361     {
362       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
363       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
364     }
365
366     ret =
367         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
368                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
369                                      &server_v6_run, plugin);
370   }
371   GNUNET_NETWORK_fdset_destroy (wrs);
372   GNUNET_NETWORK_fdset_destroy (wws);
373   GNUNET_NETWORK_fdset_destroy (wes);
374   return ret;
375 }
376
377 int
378 server_start (struct Plugin *plugin)
379 {
380   int res = GNUNET_OK;
381
382 #if BUILD_HTTPS
383   res = server_load_certificate (plugin);
384   if (res == GNUNET_SYSERR)
385   {
386     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TABORT\n");
387     return res;
388   }
389 #endif
390
391   plugin->server_v4 = NULL;
392   if (plugin->ipv4 == GNUNET_YES)
393   {
394     plugin->server_v4 = MHD_start_daemon (
395 #if VERBOSE_SERVER
396                                            MHD_USE_DEBUG |
397 #endif
398 #if BUILD_HTTPS
399                                            MHD_USE_SSL |
400 #endif
401                                            MHD_NO_FLAG, plugin->port,
402                                            &server_accept_cb, plugin,
403                                            &server_access_cb, plugin,
404                                            //MHD_OPTION_SOCK_ADDR,
405                                            //(struct sockaddr_in *)
406                                            //plugin->bind4_address,
407                                            MHD_OPTION_CONNECTION_LIMIT,
408                                            (unsigned int)
409                                            plugin->max_connections,
410 #if BUILD_HTTPS
411                                            MHD_OPTION_HTTPS_PRIORITIES,
412                                            plugin->crypto_init,
413                                            MHD_OPTION_HTTPS_MEM_KEY,
414                                            plugin->key,
415                                            MHD_OPTION_HTTPS_MEM_CERT,
416                                            plugin->cert,
417 #endif
418                                            MHD_OPTION_CONNECTION_TIMEOUT,
419                                            (unsigned int) 3,
420                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
421                                            (size_t) (2 *
422                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
423                                            MHD_OPTION_NOTIFY_COMPLETED,
424                                            &server_disconnect_cb, plugin,
425                                            MHD_OPTION_EXTERNAL_LOGGER,
426                                            server_log, NULL, MHD_OPTION_END);
427     if (plugin->server_v4 == NULL)
428       res = GNUNET_SYSERR;
429   }
430   plugin->server_v6 = NULL;
431   if (plugin->ipv6 == GNUNET_YES)
432   {
433     plugin->server_v6 = MHD_start_daemon (
434 #if VERBOSE_SERVER
435                                            MHD_USE_DEBUG |
436 #endif
437 #if BUILD_HTTPS
438                                            MHD_USE_SSL |
439 #endif
440                                            MHD_USE_IPv6, plugin->port,
441                                            &server_accept_cb, plugin,
442                                            &server_access_cb, plugin,
443                                            //MHD_OPTION_SOCK_ADDR,
444                                            //tmp,
445                                            MHD_OPTION_CONNECTION_LIMIT,
446                                            (unsigned int)
447                                            plugin->max_connections,
448 #if BUILD_HTTPS
449                                            MHD_OPTION_HTTPS_PRIORITIES,
450                                            plugin->crypto_init,
451                                            MHD_OPTION_HTTPS_MEM_KEY,
452                                            plugin->key,
453                                            MHD_OPTION_HTTPS_MEM_CERT,
454                                            plugin->cert,
455 #endif
456                                            MHD_OPTION_CONNECTION_TIMEOUT,
457                                            (unsigned int) 3,
458                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
459                                            (size_t) (2 *
460                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
461                                            MHD_OPTION_NOTIFY_COMPLETED,
462                                            &server_disconnect_cb, plugin,
463                                            MHD_OPTION_EXTERNAL_LOGGER,
464                                            server_log, NULL, MHD_OPTION_END);
465
466     if (plugin->server_v6 == NULL)
467       res = GNUNET_SYSERR;
468   }
469
470   if (plugin->server_v4 != NULL)
471     plugin->server_v4_task = server_schedule (plugin, plugin->server_v4);
472   if (plugin->server_v6 != NULL)
473     plugin->server_v6_task = server_schedule (plugin, plugin->server_v6);
474
475 #if DEBUG_HTTP
476   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
477                    "%s server component started on port %u\n", plugin->name,
478                    plugin->port);
479 #endif
480   return res;
481 }
482
483 void
484 server_stop (struct Plugin *plugin)
485 {
486   if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
487   {
488     GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
489     plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
490   }
491
492   if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
493   {
494     GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
495     plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
496   }
497
498   if (plugin->server_v4 != NULL)
499   {
500     MHD_stop_daemon (plugin->server_v4);
501     plugin->server_v4 = NULL;
502   }
503   if (plugin->server_v6 != NULL)
504   {
505     MHD_stop_daemon (plugin->server_v6);
506     plugin->server_v6 = NULL;
507   }
508
509 #if BUILD_HTTPS
510   GNUNET_free_non_null (plugin->crypto_init);
511   GNUNET_free_non_null (plugin->cert);
512   GNUNET_free_non_null (plugin->key);
513 #endif
514
515 #if DEBUG_HTTP
516   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
517                    "%s server component stopped\n", plugin->name);
518 #endif
519 }
520
521
522
523 /* end of plugin_transport_http.c */