fce6bd1a31353d0c2cebf0c5fd1d2af756983f88
[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 #define HTTP_ERROR_RESPONSE "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested URL was not found on this server.<P><HR><ADDRESS></ADDRESS></BODY></HTML>"
30 #define _RECEIVE 0
31 #define _SEND 1
32
33 static struct Plugin * p;
34
35 struct ServerConnection
36 {
37   /* _RECV or _SEND */
38   int direction;
39
40   /* should this connection get disconnected? GNUNET_YES/NO  */
41   int disconnect;
42
43   struct Session *session;
44   struct MHD_Connection *mhd_conn;
45 };
46
47 /**
48  * Function that queries MHD's select sets and
49  * starts the task waiting for them.
50  * @param plugin plugin
51  * @param daemon_handle the MHD daemon handle
52  * @param now schedule now or with MHD delay
53  * @return gnunet task identifier
54  */
55 static GNUNET_SCHEDULER_TaskIdentifier
56 server_schedule (struct Plugin *plugin,
57                  struct MHD_Daemon *daemon_handle,
58                  int now);
59
60 static void
61 server_log (void *arg, const char *fmt, va_list ap)
62 {
63   char text[1024];
64
65   vsnprintf (text, sizeof (text), fmt, ap);
66   va_end (ap);
67   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Server: %s\n", text);
68 }
69
70 /**
71  * Check if incoming connection is accepted.
72  * NOTE: Here every connection is accepted
73  * @param cls plugin as closure
74  * @param addr address of incoming connection
75  * @param addr_len address length of incoming connection
76  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
77  *
78  */
79 static int
80 server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
81 {
82   struct Plugin *plugin = cls;
83
84   if (plugin->cur_connections <= plugin->max_connections)
85     return MHD_YES;
86   else
87   {
88     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
89                 "Server: Cannot accept new connections\n");
90     return MHD_NO;
91   }
92 }
93
94
95 #if BUILD_HTTPS
96 static char *
97 server_load_file (const char *file)
98 {
99   struct GNUNET_DISK_FileHandle *gn_file;
100   uint64_t fsize;
101   char *text = NULL;
102
103   if (GNUNET_OK != GNUNET_DISK_file_size (file,
104       &fsize, GNUNET_NO, GNUNET_YES))
105     return NULL;
106   text = GNUNET_malloc (fsize + 1);
107   gn_file =
108       GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
109                              GNUNET_DISK_PERM_USER_READ);
110   if (gn_file == NULL)
111   {
112     GNUNET_free (text);
113     return NULL;
114   }
115   if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fsize))
116   {
117     GNUNET_free (text);
118     GNUNET_DISK_file_close (gn_file);
119     return NULL;
120   }
121   text[fsize] = '\0';
122   GNUNET_DISK_file_close (gn_file);
123   return text;
124 }
125 #endif
126
127
128 #if BUILD_HTTPS
129
130 static int
131 server_load_certificate (struct Plugin *plugin)
132 {
133   int res = GNUNET_OK;
134
135   char *key_file;
136   char *cert_file;
137
138   /* Get crypto init string from config
139    * If not present just use default values */
140
141   GNUNET_assert (GNUNET_OK ==
142                  GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
143                                                         plugin->name,
144                                                         "CRYPTO_INIT",
145                                                         &plugin->crypto_init));
146
147   if (GNUNET_OK !=
148       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
149                                                "KEY_FILE", &key_file))
150   {
151     key_file = GNUNET_strdup ("https_key.key");
152   }
153
154   if (GNUNET_OK !=
155       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
156                                                "CERT_FILE", &cert_file))
157   {
158     GNUNET_asprintf (&cert_file, "%s", "https_cert.crt");
159   }
160
161   /* read key & certificates from file */
162   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163               "Loading TLS certificate from key-file `%s' cert-file`%s'\n",
164               key_file, cert_file);
165
166   plugin->key = server_load_file (key_file);
167   plugin->cert = server_load_file (cert_file);
168
169   if ((plugin->key == NULL) || (plugin->cert == NULL))
170   {
171     struct GNUNET_OS_Process *cert_creation;
172
173     GNUNET_free_non_null (plugin->key);
174     plugin->key = NULL;
175     GNUNET_free_non_null (plugin->cert);
176     plugin->cert = NULL;
177
178 #if VERBOSE_SERVER
179     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
180                 "No usable TLS certificate found, creating certificate\n");
181 #endif
182     errno = 0;
183     cert_creation =
184         GNUNET_OS_start_process (GNUNET_NO, NULL, NULL,
185                                  "gnunet-transport-certificate-creation",
186                                  "gnunet-transport-certificate-creation",
187                                  key_file, cert_file, NULL);
188     if (cert_creation == NULL)
189     {
190       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
191                        _
192                        ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
193       GNUNET_free (key_file);
194       GNUNET_free (cert_file);
195
196       GNUNET_free_non_null (plugin->key);
197       plugin->key = NULL;
198       GNUNET_free_non_null (plugin->cert);
199       plugin->cert = NULL;
200       GNUNET_free_non_null (plugin->crypto_init);
201       plugin->crypto_init = NULL;
202
203       return GNUNET_SYSERR;
204     }
205     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation));
206     GNUNET_OS_process_destroy (cert_creation);
207
208     plugin->key = server_load_file (key_file);
209     plugin->cert = server_load_file (cert_file);
210   }
211
212   if ((plugin->key == NULL) || (plugin->cert == NULL))
213   {
214     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
215                      _
216                      ("No usable TLS certificate found and creating one failed!\n"),
217                      "transport-https");
218     GNUNET_free (key_file);
219     GNUNET_free (cert_file);
220
221     GNUNET_free_non_null (plugin->key);
222     plugin->key = NULL;
223     GNUNET_free_non_null (plugin->cert);
224     plugin->cert = NULL;
225     GNUNET_free_non_null (plugin->crypto_init);
226     plugin->crypto_init = NULL;
227
228     return GNUNET_SYSERR;
229   }
230   GNUNET_free (key_file);
231   GNUNET_free (cert_file);
232   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
233   return res;
234 }
235 #endif
236
237
238 /**
239  * Reschedule the execution of both IPv4 and IPv6 server
240  * @param plugin the plugin
241  * @param server which server to schedule v4 or v6?
242  * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait
243  * until timeout
244  */
245
246 static void
247 server_reschedule (struct Plugin *plugin, struct MHD_Daemon *server, int now)
248 {
249   if ((server == plugin->server_v4) && (plugin->server_v4 != NULL))
250   {
251     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
252     {
253       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
254       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
255     }
256     plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now);
257   }
258
259   if ((server == plugin->server_v6) && (plugin->server_v6 != NULL))
260   {
261     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
262     {
263       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
264       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
265     }
266     plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now);
267   }
268 }
269
270 /**
271  * Callback called by MessageStreamTokenizer when a message has arrived
272  * @param cls current session as closure
273  * @param client clien
274  * @param message the message to be forwarded to transport service
275  */
276 static int
277 server_receive_mst_cb (void *cls, void *client,
278                        const struct GNUNET_MessageHeader *message)
279 {
280   struct Session *s = cls;
281
282   GNUNET_assert (NULL != p);
283   if (GNUNET_NO == exist_session(p, s))
284     return GNUNET_OK;
285
286   struct Plugin *plugin = s->plugin;
287   struct GNUNET_TIME_Relative delay;
288
289   delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen);
290
291   s->next_receive =
292       GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay);
293
294   if (delay.rel_value > 0)
295   {
296     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
297                      "Server: peer `%s' address `%s' next read delayed for %llu ms\n",
298                      GNUNET_i2s (&s->target),
299                      http_plugin_address_to_string (NULL, s->addr, s->addrlen),
300                      delay);
301   }
302   return GNUNET_OK;
303 }
304
305 /**
306  * Callback called by MHD when it needs data to send
307  * @param cls current session
308  * @param pos position in buffer
309  * @param buf the buffer to write data to
310  * @param max max number of bytes available in buffer
311  * @return bytes written to buffer
312  */
313 static ssize_t
314 server_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
315 {
316   struct Session *s = cls;
317   GNUNET_assert (NULL != p);
318   if (GNUNET_NO == exist_session(p, s))
319     return 0;
320
321   struct HTTP_Message *msg;
322   int bytes_read = 0;
323
324   //static int c = 0;
325   msg = s->msg_head;
326   if (msg != NULL)
327   {
328     /* sending */
329     if ((msg->size - msg->pos) <= max)
330     {
331       memcpy (buf, &msg->buf[msg->pos], (msg->size - msg->pos));
332       bytes_read = msg->size - msg->pos;
333       msg->pos += (msg->size - msg->pos);
334     }
335     else
336     {
337       memcpy (buf, &msg->buf[msg->pos], max);
338       msg->pos += max;
339       bytes_read = max;
340     }
341
342     /* removing message */
343     if (msg->pos == msg->size)
344     {
345       if (NULL != msg->transmit_cont)
346         msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
347       GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
348       GNUNET_free (msg);
349     }
350   }
351
352   struct Plugin *plugin = s->plugin;
353   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
354                    "Server: %X: sent %u bytes\n", s, bytes_read);
355
356   return bytes_read;
357 }
358
359 static struct ServerConnection *
360 server_lookup_session (struct Plugin *plugin,
361                        struct MHD_Connection *mhd_connection, const char *url,
362                        const char *method)
363 {
364   struct Session *s = NULL;
365   struct Session *t;
366   struct ServerConnection *sc = NULL;
367   const union MHD_ConnectionInfo *conn_info;
368   struct GNUNET_ATS_Information ats;
369   struct IPv4HttpAddress a4;
370   struct IPv6HttpAddress a6;
371   struct sockaddr_in *s4;
372   struct sockaddr_in6 *s6;
373   void *a;
374   size_t a_len;
375   struct GNUNET_PeerIdentity target;
376   int check = GNUNET_NO;
377   uint32_t tag = 0;
378   int direction = GNUNET_SYSERR;
379
380   conn_info = MHD_get_connection_info (mhd_connection,
381                                MHD_CONNECTION_INFO_CLIENT_ADDRESS);
382   if ((conn_info->client_addr->sa_family != AF_INET) &&
383       (conn_info->client_addr->sa_family != AF_INET6))
384     return NULL;
385
386   if ((strlen (&url[1]) >= 105) && (url[104] == ';'))
387   {
388     char hash[104];
389     char *tagc = (char *) &url[105];
390
391     memcpy (&hash, &url[1], 103);
392     hash[103] = '\0';
393     if (GNUNET_OK ==
394         GNUNET_CRYPTO_hash_from_string ((const char *) &hash,
395                                         &(target.hashPubKey)))
396     {
397       tag = strtoul (tagc, NULL, 10);
398       if (tagc > 0)
399         check = GNUNET_YES;
400     }
401   }
402
403   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
404     direction = _RECEIVE;
405   else if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
406     direction = _SEND;
407   else
408   {
409     GNUNET_break_op (0);
410     goto error;
411   }
412
413
414   if (check == GNUNET_NO)
415     goto error;
416
417   plugin->cur_connections++;
418   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
419                    "Server: New %s connection from %s with tag %u\n",
420                    method,
421                    GNUNET_i2s (&target), tag);
422
423   /* find duplicate session */
424   t = plugin->head;
425   while (t != NULL)
426   {
427     if ((t->inbound) &&
428         (0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity)))
429         &&
430         /* FIXME add source address comparison */
431         (t->tag == tag))
432       break;
433     t = t->next;
434   }
435   if (t != NULL)
436   {
437     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
438                      "Server: Duplicate session, dismissing new connection from peer `%s'\n",
439                      GNUNET_i2s (&target));
440     goto error;
441   }
442
443   /* find semi-session */
444   t = plugin->server_semi_head;
445
446   while (t != NULL)
447   {
448     /* FIXME add source address comparison */
449     if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity)))
450         && (t->tag == tag))
451     {
452       break;
453     }
454     t = t->next;
455   }
456
457   if (t == NULL)
458     goto create;
459   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
460                    "Server: Found existing semi-session for `%s'\n",
461                    GNUNET_i2s (&target));
462
463   if ((direction == _SEND) && (t->server_send != NULL))
464   {
465     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
466                      "Server: Duplicate GET session, dismissing new connection from peer `%s'\n",
467                      GNUNET_i2s (&target));
468     goto error;
469   }
470   else
471   {
472     s = t;
473     GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head,
474                                  plugin->server_semi_tail, s);
475     GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
476     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
477                      "Server: Found matching semi-session, merging session for peer `%s'\n",
478                      GNUNET_i2s (&target));
479
480     plugin->inbound_sessions ++;
481     GNUNET_STATISTICS_set (plugin->env->stats,
482         "# HTTP inbound sessions",
483         plugin->inbound_sessions,
484         GNUNET_NO);
485     GNUNET_assert (NULL != s);
486     goto found;
487   }
488   if ((direction == _RECEIVE) && (t->server_recv != NULL))
489   {
490     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
491                      "Server: Duplicate PUT session, dismissing new connection from peer `%s'\n",
492                      GNUNET_i2s (&target));
493     goto error;
494   }
495   else
496   {
497     s = t;
498     GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head,
499                                  plugin->server_semi_tail, s);
500     GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
501     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
502                      "Server: Found matching semi-session, merging session for peer `%s'\n",
503                      GNUNET_i2s (&target));
504     plugin->inbound_sessions ++;
505     GNUNET_STATISTICS_set (plugin->env->stats,
506         "# HTTP inbound sessions",
507         plugin->inbound_sessions,
508         GNUNET_NO);
509     GNUNET_assert (NULL != s);
510     goto found;
511   }
512
513 create:
514 /* create new session */
515   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
516                    "Server: Creating new session for peer `%s' \n",
517                    GNUNET_i2s (&target));
518   switch (conn_info->client_addr->sa_family)
519   {
520   case (AF_INET):
521     s4 = ((struct sockaddr_in *) conn_info->client_addr);
522     a4.u4_port = s4->sin_port;
523     memcpy (&a4.ipv4_addr, &s4->sin_addr, sizeof (struct in_addr));
524     a = &a4;
525     a_len = sizeof (struct IPv4HttpAddress);
526     ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) s4, sizeof (struct sockaddr_in));
527     break;
528   case (AF_INET6):
529     s6 = ((struct sockaddr_in6 *) conn_info->client_addr);
530     a6.u6_port = s6->sin6_port;
531     memcpy (&a6.ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr));
532     a = &a6;
533     a_len = sizeof (struct IPv6HttpAddress);
534     ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) s6, sizeof (struct sockaddr_in6));
535     break;
536   default:
537     GNUNET_break (0);
538     goto error;
539   }
540   s = create_session (plugin, &target, a, a_len);
541   GNUNET_assert (NULL != s);
542   s->ats_address_network_type = ats.value;
543
544   s->inbound = GNUNET_YES;
545   s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS;
546   s->tag = tag;
547   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
548     s->server_recv = s;
549   if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
550     s->server_send = s;
551   GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head,
552                                plugin->server_semi_tail, s);
553   goto found;
554
555 error:
556   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
557                    "Server: Invalid connection request\n");
558   return NULL;
559
560 found:
561   sc = GNUNET_malloc (sizeof (struct ServerConnection));
562   sc->mhd_conn = mhd_connection;
563   sc->direction = direction;
564   sc->session = s;
565   if (direction == _SEND)
566     s->server_send = sc;
567   if (direction == _RECEIVE)
568     s->server_recv = sc;
569
570 #if MHD_VERSION >= 0x00090E00
571   int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
572
573   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
574                    "Server: Setting timeout for %X to %u sec.\n", sc, to);
575   MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to);
576
577   struct MHD_Daemon *d = NULL;
578
579   if (s->addrlen == sizeof (struct IPv6HttpAddress))
580     d = plugin->server_v6;
581   if (s->addrlen == sizeof (struct IPv4HttpAddress))
582     d = plugin->server_v4;
583
584   server_reschedule (plugin, d, GNUNET_NO);
585 #endif
586   return sc;
587 }
588
589 /**
590  * Process GET or PUT request received via MHD.  For
591  * GET, queue response that will send back our pending
592  * messages.  For PUT, process incoming data and send
593  * to GNUnet core.  In either case, check if a session
594  * already exists and create a new one if not.
595  */
596 static int
597 server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
598                   const char *url, const char *method, const char *version,
599                   const char *upload_data, size_t * upload_data_size,
600                   void **httpSessionCache)
601 {
602   struct Plugin *plugin = cls;
603   struct ServerConnection *sc = *httpSessionCache;
604   struct Session *s;
605   struct MHD_Response *response;
606   int res = MHD_YES;
607
608   GNUNET_assert (cls != NULL);
609   /* new connection */
610   if (sc == NULL)
611   {
612     sc = server_lookup_session (plugin, mhd_connection, url, method);
613     if (sc != NULL)
614       (*httpSessionCache) = sc;
615     else
616     {
617       response =
618           MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),
619                                          HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
620       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
621       MHD_destroy_response (response);
622       return res;
623     }
624   }
625
626   /* existing connection */
627   sc = (*httpSessionCache);
628   s = sc->session;
629
630   GNUNET_assert (NULL != s);
631
632   /* connection is to be disconnected */
633   if (sc->disconnect == GNUNET_YES)
634   {
635     /* Sent HTTP/1.1: 200 OK as PUT Response\ */
636     response =
637         MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!",
638                                        MHD_NO, MHD_NO);
639     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
640     MHD_destroy_response (response);
641     return MHD_YES;
642   }
643
644   GNUNET_assert (s != NULL);
645   /* Check if both directions are connected */
646   if ((sc->session->server_recv == NULL) || (sc->session->server_send == NULL))
647   {
648     /* Delayed read from since not both semi-connections are connected */
649     return MHD_YES;
650   }
651
652   if (sc->direction == _SEND)
653   {
654     response =
655         MHD_create_response_from_callback (-1, 32 * 1024, &server_send_callback,
656                                            s, NULL);
657     MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
658     MHD_destroy_response (response);
659     return MHD_YES;
660   }
661   if (sc->direction == _RECEIVE)
662   {
663     if (*upload_data_size == 0)
664     {
665       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
666                        "Server: Peer `%s' PUT on address `%s' connected\n",
667                        GNUNET_i2s (&s->target),
668                        http_plugin_address_to_string (NULL, s->addr,
669                                                       s->addrlen));
670       return MHD_YES;
671     }
672
673     /* Receiving data */
674     if ((*upload_data_size > 0))
675     {
676       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
677                        "Server: peer `%s' PUT on address `%s' received %u bytes\n",
678                        GNUNET_i2s (&s->target),
679                        http_plugin_address_to_string (NULL, s->addr,
680                                                       s->addrlen),
681                        *upload_data_size);
682       struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
683
684       if ((s->next_receive.abs_value <= now.abs_value))
685       {
686         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
687                          "Server: %X: PUT with %u bytes forwarded to MST\n", s,
688                          *upload_data_size);
689         if (s->msg_tk == NULL)
690         {
691           s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s);
692         }
693             GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data,
694                                        *upload_data_size, GNUNET_NO, GNUNET_NO);
695
696 #if MHD_VERSION >= 0x00090E00
697         int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
698         struct ServerConnection *t = NULL;
699
700         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
701                          "Server: Received %u bytes\n", *upload_data_size);
702         /* Setting timeouts for other connections */
703         if (s->server_recv != NULL)
704         {
705           t = s->server_recv;
706           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
707                            "Server: Setting timeout for %X to %u sec.\n", t,
708                            to);
709           MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
710                                      to);
711         }
712         if (s->server_send != NULL)
713         {
714           t = s->server_send;
715           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
716                            "Server: Setting timeout for %X to %u sec.\n", t,
717                            to);
718           MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
719                                      to);
720         }
721         struct MHD_Daemon *d = NULL;
722
723         if (s->addrlen == sizeof (struct IPv6HttpAddress))
724           d = plugin->server_v6;
725         if (s->addrlen == sizeof (struct IPv4HttpAddress))
726           d = plugin->server_v4;
727         server_reschedule (plugin, d, GNUNET_NO);
728 #endif
729         (*upload_data_size) = 0;
730       }
731       else
732       {
733         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
734                     "Server: %X no inbound bandwidth available! Next read was delayed by %llu ms\n",
735                     s, now.abs_value - s->next_receive.abs_value);
736       }
737       return MHD_YES;
738     }
739     else
740       return MHD_NO;
741   }
742   return res;
743 }
744
745 static void
746 server_disconnect_cb (void *cls, struct MHD_Connection *connection,
747                       void **httpSessionCache)
748 {
749   struct ServerConnection *sc = *httpSessionCache;
750   struct ServerConnection *tc = NULL;
751   struct Session *s = NULL;
752   struct Session *t = NULL;
753   struct Plugin *plugin = NULL;
754
755   if (sc == NULL)
756     return;
757
758   s = sc->session;
759   GNUNET_assert (NULL != s);
760   GNUNET_assert (NULL != p);
761   if (GNUNET_NO == exist_session(p, s))
762     return;
763
764   plugin = s->plugin;
765   if (sc->direction == _SEND)
766   {
767
768     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
769                      "Server: %X peer `%s' GET on address `%s' disconnected\n",
770                      s->server_send, GNUNET_i2s (&s->target),
771                      http_plugin_address_to_string (NULL, s->addr, s->addrlen));
772
773     s->server_send = NULL;
774
775     if (s->server_recv != NULL)
776     {
777       tc = s->server_recv;
778       tc->disconnect = GNUNET_YES;
779 #if MHD_VERSION >= 0x00090E00
780       MHD_set_connection_option (sc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
781                                  1);
782 #endif
783     }
784   }
785   if (sc->direction == _RECEIVE)
786   {
787     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
788                      "Server: %X peer `%s' PUT on address `%s' disconnected\n",
789                      s->server_recv, GNUNET_i2s (&s->target),
790                      http_plugin_address_to_string (NULL, s->addr, s->addrlen));
791     s->server_recv = NULL;
792     if (s->server_send != NULL)
793     {
794       tc = s->server_send;
795       tc->disconnect = GNUNET_YES;
796 #if MHD_VERSION >= 0x00090E00
797       MHD_set_connection_option (sc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
798                                  1);
799 #endif
800     }
801     if (s->msg_tk != NULL)
802     {
803       GNUNET_SERVER_mst_destroy (s->msg_tk);
804       s->msg_tk = NULL;
805     }
806   }
807   GNUNET_free (sc);
808
809   t = plugin->server_semi_head;
810   while (t != NULL)
811   {
812     if (t == s)
813     {
814       GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head,
815                                    plugin->server_semi_tail, s);
816       GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
817       break;
818     }
819     t = t->next;
820   }
821   plugin->cur_connections--;
822
823   struct MHD_Daemon *d = NULL;
824
825   if (s->addrlen == sizeof (struct IPv6HttpAddress))
826     d = plugin->server_v6;
827   if (s->addrlen == sizeof (struct IPv4HttpAddress))
828     d = plugin->server_v4;
829   server_reschedule (plugin, d, GNUNET_NO);
830
831   if ((s->server_send == NULL) && (s->server_recv == NULL))
832   {
833     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
834                      "Server: peer `%s' on address `%s' disconnected\n",
835                      GNUNET_i2s (&s->target),
836                      http_plugin_address_to_string (NULL, s->addr, s->addrlen));
837     if (s->msg_tk != NULL)
838     {
839       GNUNET_SERVER_mst_destroy (s->msg_tk);
840       s->msg_tk = NULL;
841     }
842
843     GNUNET_assert (plugin->inbound_sessions > 0);
844     plugin->inbound_sessions --;
845     GNUNET_STATISTICS_set (plugin->env->stats,
846         "# HTTP inbound sessions",
847         plugin->inbound_sessions, GNUNET_NO);
848
849     notify_session_end (s->plugin, &s->target, s);
850   }
851 }
852
853 int
854 server_disconnect (struct Session *s)
855 {
856   struct Plugin *plugin = s->plugin;
857   struct Session *t = plugin->head;
858
859   while (t != NULL)
860   {
861     if (t->inbound == GNUNET_YES)
862     {
863       if (t->server_send != NULL)
864       {
865         ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES;
866       }
867       if (t->server_send != NULL)
868       {
869         ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES;
870       }
871     }
872     t = t->next;
873   }
874   return GNUNET_OK;
875 }
876
877 int
878 server_send (struct Session *s, struct HTTP_Message *msg)
879 {
880   GNUNET_CONTAINER_DLL_insert (s->msg_head, s->msg_tail, msg);
881
882   if (s->addrlen == sizeof (struct IPv4HttpAddress))
883   {
884     server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES);
885   }
886   else if (s->addrlen == sizeof (struct IPv6HttpAddress))
887   {
888     server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES);
889   }
890   else
891     return GNUNET_SYSERR;
892   return GNUNET_OK;
893 }
894
895
896
897 /**
898  * Call MHD IPv4 to process pending requests and then go back
899  * and schedule the next run.
900  * @param cls plugin as closure
901  * @param tc task context
902  */
903 static void
904 server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
905 {
906   struct Plugin *plugin = cls;
907
908   GNUNET_assert (cls != NULL);
909
910   plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
911
912   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
913     return;
914 #if 0
915   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
916                    "Running IPv4 server\n");
917 #endif
918   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4));
919   if (plugin->server_v4 != NULL)
920     plugin->server_v4_task =
921         server_schedule (plugin, plugin->server_v4, GNUNET_NO);
922 }
923
924
925 /**
926  * Call MHD IPv6 to process pending requests and then go back
927  * and schedule the next run.
928  * @param cls plugin as closure
929  * @param tc task context
930  */
931 static void
932 server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
933 {
934   struct Plugin *plugin = cls;
935
936   GNUNET_assert (cls != NULL);
937
938   plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
939
940   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
941     return;
942 #if 0
943   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
944                    "Running IPv6 server\n");
945 #endif
946   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6));
947   if (plugin->server_v6 != NULL)
948     plugin->server_v6_task =
949         server_schedule (plugin, plugin->server_v6, GNUNET_NO);
950 }
951
952 /**
953  * Function that queries MHD's select sets and
954  * starts the task waiting for them.
955  * @param plugin plugin
956  * @param daemon_handle the MHD daemon handle
957  * @return gnunet task identifier
958  */
959 static GNUNET_SCHEDULER_TaskIdentifier
960 server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle,
961                  int now)
962 {
963   GNUNET_SCHEDULER_TaskIdentifier ret;
964   fd_set rs;
965   fd_set ws;
966   fd_set es;
967   struct GNUNET_NETWORK_FDSet *wrs;
968   struct GNUNET_NETWORK_FDSet *wws;
969   struct GNUNET_NETWORK_FDSet *wes;
970   int max;
971   unsigned MHD_LONG_LONG timeout;
972   static unsigned long long last_timeout = 0;
973   int haveto;
974
975   struct GNUNET_TIME_Relative tv;
976
977   ret = GNUNET_SCHEDULER_NO_TASK;
978   FD_ZERO (&rs);
979   FD_ZERO (&ws);
980   FD_ZERO (&es);
981   wrs = GNUNET_NETWORK_fdset_create ();
982   wes = GNUNET_NETWORK_fdset_create ();
983   wws = GNUNET_NETWORK_fdset_create ();
984   max = -1;
985   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
986   haveto = MHD_get_timeout (daemon_handle, &timeout);
987   if (haveto == MHD_YES)
988   {
989     if (timeout != last_timeout)
990     {
991 #if VERBOSE_SERVER
992       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
993                        "SELECT Timeout changed from %llu to %llu\n",
994                        last_timeout, timeout);
995 #endif
996       last_timeout = timeout;
997     }
998     tv.rel_value = (uint64_t) timeout;
999   }
1000   else
1001     tv = GNUNET_TIME_UNIT_SECONDS;
1002   /* Force immediate run, since we have outbound data to send */
1003   if (now == GNUNET_YES)
1004     tv = GNUNET_TIME_UNIT_MILLISECONDS;
1005   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1006   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1007   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1008
1009   if (daemon_handle == plugin->server_v4)
1010   {
1011     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1012     {
1013       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1014       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1015     }
1016 #if 0
1017     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1018                      "Scheduling IPv4 server task in %llu ms\n", tv);
1019 #endif
1020     ret =
1021         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1022                                      tv, wrs, wws,
1023                                      &server_v4_run, plugin);
1024   }
1025   if (daemon_handle == plugin->server_v6)
1026   {
1027     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1028     {
1029       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1030       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1031     }
1032 #if 0
1033     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1034                      "Scheduling IPv6 server task in %llu ms\n", tv);
1035 #endif
1036     ret =
1037         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1038                                      tv, wrs, wws,
1039                                      &server_v6_run, plugin);
1040   }
1041   GNUNET_NETWORK_fdset_destroy (wrs);
1042   GNUNET_NETWORK_fdset_destroy (wws);
1043   GNUNET_NETWORK_fdset_destroy (wes);
1044   return ret;
1045 }
1046
1047 int
1048 server_start (struct Plugin *plugin)
1049 {
1050   int res = GNUNET_OK;
1051   unsigned int timeout;
1052   p = plugin;
1053   GNUNET_assert (NULL != plugin);
1054
1055 #if BUILD_HTTPS
1056   res = server_load_certificate (plugin);
1057   if (res == GNUNET_SYSERR)
1058   {
1059     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1060                      "Could not load or create server certificate! Loading plugin failed!\n");
1061     return res;
1062   }
1063 #endif
1064
1065
1066 #if MHD_VERSION >= 0x00090E00
1067   timeout = HTTP_NOT_VALIDATED_TIMEOUT.rel_value / 1000;
1068   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1069                    "MHD can set timeout per connection! Default time out %u sec.\n",
1070                    timeout);
1071 #else
1072   timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000;
1073   GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1074                    "MHD cannot set timeout per connection! Default time out %u sec.\n",
1075                    timeout);
1076 #endif
1077   plugin->server_v4 = NULL;
1078   if (plugin->ipv4 == GNUNET_YES)
1079   {
1080     plugin->server_v4 = MHD_start_daemon (
1081 #if VERBOSE_SERVER
1082                                            MHD_USE_DEBUG |
1083 #endif
1084 #if BUILD_HTTPS
1085                                            MHD_USE_SSL |
1086 #endif
1087                                            MHD_NO_FLAG, plugin->port,
1088                                            &server_accept_cb, plugin,
1089                                            &server_access_cb, plugin,
1090                                            MHD_OPTION_SOCK_ADDR,
1091                                            (struct sockaddr_in *)
1092                                            plugin->server_addr_v4,
1093                                            MHD_OPTION_CONNECTION_LIMIT,
1094                                            (unsigned int)
1095                                            plugin->max_connections,
1096 #if BUILD_HTTPS
1097                                            MHD_OPTION_HTTPS_PRIORITIES,
1098                                            plugin->crypto_init,
1099                                            MHD_OPTION_HTTPS_MEM_KEY,
1100                                            plugin->key,
1101                                            MHD_OPTION_HTTPS_MEM_CERT,
1102                                            plugin->cert,
1103 #endif
1104                                            MHD_OPTION_CONNECTION_TIMEOUT,
1105                                            timeout,
1106                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1107                                            (size_t) (2 *
1108                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1109                                            MHD_OPTION_NOTIFY_COMPLETED,
1110                                            &server_disconnect_cb, plugin,
1111                                            MHD_OPTION_EXTERNAL_LOGGER,
1112                                            server_log, NULL, MHD_OPTION_END);
1113   }
1114   plugin->server_v6 = NULL;
1115   if (plugin->ipv6 == GNUNET_YES)
1116   {
1117     plugin->server_v6 = MHD_start_daemon (
1118 #if VERBOSE_SERVER
1119                                            MHD_USE_DEBUG |
1120 #endif
1121 #if BUILD_HTTPS
1122                                            MHD_USE_SSL |
1123 #endif
1124                                            MHD_USE_IPv6, plugin->port,
1125                                            &server_accept_cb, plugin,
1126                                            &server_access_cb, plugin,
1127                                            MHD_OPTION_SOCK_ADDR,
1128                                            (struct sockaddr_in6 *)
1129                                            plugin->server_addr_v6,
1130                                            MHD_OPTION_CONNECTION_LIMIT,
1131                                            (unsigned int)
1132                                            plugin->max_connections,
1133 #if BUILD_HTTPS
1134                                            MHD_OPTION_HTTPS_PRIORITIES,
1135                                            plugin->crypto_init,
1136                                            MHD_OPTION_HTTPS_MEM_KEY,
1137                                            plugin->key,
1138                                            MHD_OPTION_HTTPS_MEM_CERT,
1139                                            plugin->cert,
1140 #endif
1141                                            MHD_OPTION_CONNECTION_TIMEOUT,
1142                                            timeout,
1143                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1144                                            (size_t) (2 *
1145                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1146                                            MHD_OPTION_NOTIFY_COMPLETED,
1147                                            &server_disconnect_cb, plugin,
1148                                            MHD_OPTION_EXTERNAL_LOGGER,
1149                                            server_log, NULL, MHD_OPTION_END);
1150
1151   }
1152
1153   if ((plugin->ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL))
1154   {
1155     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1156                      "Failed to start %s IPv4 server component on port %u\n",
1157                      plugin->name, plugin->port);
1158     return GNUNET_SYSERR;
1159   }
1160   server_reschedule (plugin, plugin->server_v4, GNUNET_NO);
1161
1162   if ((plugin->ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL))
1163   {
1164     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1165                      "Failed to start %s IPv6 server component on port %u\n",
1166                      plugin->name, plugin->port);
1167     return GNUNET_SYSERR;
1168   }
1169   server_reschedule (plugin, plugin->server_v6, GNUNET_NO);
1170   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1171                    "%s server component started on port %u\n", plugin->name,
1172                    plugin->port);
1173   return res;
1174 }
1175
1176 void
1177 server_stop (struct Plugin *plugin)
1178 {
1179   struct Session *s = NULL;
1180   struct Session *t = NULL;
1181
1182   p = NULL;
1183
1184   struct MHD_Daemon *server_v4_tmp = plugin->server_v4;
1185
1186   plugin->server_v4 = NULL;
1187   struct MHD_Daemon *server_v6_tmp = plugin->server_v6;
1188
1189   plugin->server_v6 = NULL;
1190
1191   if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1192   {
1193     GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1194     plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1195   }
1196
1197   if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1198   {
1199     GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1200     plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1201   }
1202
1203   if (server_v6_tmp != NULL)
1204   {
1205     MHD_stop_daemon (server_v4_tmp);
1206   }
1207   if (server_v6_tmp != NULL)
1208   {
1209     MHD_stop_daemon (server_v6_tmp);
1210   }
1211
1212   /* cleaning up semi-sessions never propagated */
1213   s = plugin->server_semi_head;
1214   while (s != NULL)
1215   {
1216 #if VERBOSE_SERVER
1217     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1218                      "Deleting semi-sessions %p\n", s);
1219 #endif
1220     t = s->next;
1221     struct HTTP_Message *msg = s->msg_head;
1222     struct HTTP_Message *tmp = NULL;
1223
1224     while (msg != NULL)
1225     {
1226       tmp = msg->next;
1227
1228       GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
1229       if (msg->transmit_cont != NULL)
1230       {
1231         msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
1232       }
1233       GNUNET_free (msg);
1234       msg = tmp;
1235     }
1236
1237     delete_session (s);
1238     s = t;
1239   }
1240
1241 #if BUILD_HTTPS
1242   GNUNET_free_non_null (plugin->crypto_init);
1243   GNUNET_free_non_null (plugin->cert);
1244   GNUNET_free_non_null (plugin->key);
1245 #endif
1246
1247   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1248                    "%s server component stopped\n", plugin->name);
1249 }
1250
1251
1252
1253 /* end of plugin_transport_http.c */