98fbfda1c71d62411485ee9ae0e1c7c7892047cb
[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 void
34 server_log (void *arg, const char *fmt, va_list ap)
35 {
36   char text[1024];
37
38   vsnprintf (text, sizeof (text), fmt, ap);
39   va_end (ap);
40   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "server: %s\n", text);
41 }
42
43 struct ServerConnection
44 {
45   /* _RECV or _SEND */
46   int direction;
47
48   /* should this connection get disconnected? GNUNET_YES/NO  */
49   int disconnect;
50
51   struct Session *session;
52   struct MHD_Connection * mhd_conn;
53 };
54
55 /**
56  * Check if incoming connection is accepted.
57  * NOTE: Here every connection is accepted
58  * @param cls plugin as closure
59  * @param addr address of incoming connection
60  * @param addr_len address length of incoming connection
61  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
62  *
63  */
64 static int
65 server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
66 {
67   struct Plugin * plugin = cls;
68
69   if (plugin->cur_connections <= plugin->max_connections)
70     return MHD_YES;
71   else
72   {
73     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "server: Cannot accept new connections\n");
74     return MHD_NO;
75   }
76 }
77
78
79 /**
80  * Callback called by MHD when it needs data to send
81  * @param cls current session
82  * @param pos position in buffer
83  * @param buf the buffer to write data to
84  * @param max max number of bytes available in buffer
85  * @return bytes written to buffer
86  */
87 #if 0
88 static ssize_t
89 server_send_cb (void *cls, uint64_t pos, char *buf, size_t max)
90 {
91
92   return 0;
93 }
94 #endif
95
96
97 #if BUILD_HTTPS
98 static char *
99 server_load_file (const char *file)
100 {
101   struct GNUNET_DISK_FileHandle *gn_file;
102   struct stat fstat;
103   char *text = NULL;
104
105   if (0 != STAT (file, &fstat))
106     return NULL;
107   text = GNUNET_malloc (fstat.st_size + 1);
108   gn_file =
109       GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
110                              GNUNET_DISK_PERM_USER_READ);
111   if (gn_file == NULL)
112   {
113     GNUNET_free (text);
114     return NULL;
115   }
116   if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fstat.st_size))
117   {
118     GNUNET_free (text);
119     GNUNET_DISK_file_close (gn_file);
120     return NULL;
121   }
122   text[fstat.st_size] = '\0';
123   GNUNET_DISK_file_close (gn_file);
124   return text;
125 }
126 #endif
127
128
129 #if BUILD_HTTPS
130
131 static int
132 server_load_certificate (struct Plugin *plugin)
133 {
134   int res = GNUNET_OK;
135
136   char *key_file;
137   char *cert_file;
138
139   /* Get crypto init string from config
140    * If not present just use default values */
141   GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
142                                          "CRYPTO_INIT", &plugin->crypto_init);
143
144   if (GNUNET_OK !=
145       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
146                                                "KEY_FILE", &key_file))
147   {
148     key_file = "https_key.key";
149   }
150
151   if (GNUNET_OK !=
152       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
153                                                "CERT_FILE", &cert_file))
154   {
155     cert_file = "https_cert.crt";
156   }
157
158   /* read key & certificates from file */
159 #if VERBOSE_SERVER
160   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
161               "Loading TLS certificate from key-file `%s' cert-file`%s'\n",
162               key_file, cert_file);
163 #endif
164
165   plugin->key = server_load_file (key_file);
166   plugin->cert = server_load_file (cert_file);
167
168   if ((plugin->key == NULL) || (plugin->cert == NULL))
169   {
170     struct GNUNET_OS_Process *cert_creation;
171
172     GNUNET_free_non_null (plugin->key);
173     plugin->key = NULL;
174     GNUNET_free_non_null (plugin->cert);
175     plugin->cert = NULL;
176
177 #if VERBOSE_SERVER
178     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
179                 "No usable TLS certificate found, creating certificate\n");
180 #endif
181     errno = 0;
182     cert_creation =
183         GNUNET_OS_start_process (NULL, NULL,
184                                  "gnunet-transport-certificate-creation",
185                                  "gnunet-transport-certificate-creation",
186                                  key_file, cert_file, NULL);
187     if (cert_creation == NULL)
188     {
189       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
190                        _
191                        ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
192       GNUNET_free (key_file);
193       GNUNET_free (cert_file);
194
195       GNUNET_free_non_null (plugin->key);
196       GNUNET_free_non_null (plugin->cert);
197       GNUNET_free_non_null (plugin->crypto_init);
198
199       return GNUNET_SYSERR;
200     }
201     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation));
202     GNUNET_OS_process_close (cert_creation);
203
204     plugin->key = server_load_file (key_file);
205     plugin->cert = server_load_file (cert_file);
206   }
207
208   if ((plugin->key == NULL) || (plugin->cert == NULL))
209   {
210     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
211                      _
212                      ("No usable TLS certificate found and creating one failed!\n"),
213                      "transport-https");
214     GNUNET_free (key_file);
215     GNUNET_free (cert_file);
216
217     GNUNET_free_non_null (plugin->key);
218     GNUNET_free_non_null (plugin->cert);
219     GNUNET_free_non_null (plugin->crypto_init);
220
221     return GNUNET_SYSERR;
222   }
223   GNUNET_free (key_file);
224   GNUNET_free (cert_file);
225 #if DEBUG_HTTP
226   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
227 #endif
228
229   return res;
230 }
231 #endif
232
233
234 /**
235  * Callback called by MessageStreamTokenizer when a message has arrived
236  * @param cls current session as closure
237  * @param client clien
238  * @param message the message to be forwarded to transport service
239  */
240 static void
241 server_receive_mst_cb (void *cls, void *client,
242                   const struct GNUNET_MessageHeader *message)
243 {
244   struct Session *s = cls;
245   struct Plugin *plugin = s->plugin;
246   struct GNUNET_TIME_Relative delay;
247
248   delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen);
249
250   s->delay = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), delay);
251
252   if (GNUNET_TIME_absolute_get().abs_value < s->delay.abs_value)
253   {
254 #if VERBOSE_CLIENT
255     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, "Server: peer `%s' address `%s' next read delayed for %llu ms\n",
256                 GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen), delay);
257 #endif
258   }
259 }
260
261 /**
262  * Callback called by MHD when it needs data to send
263  * @param cls current session
264  * @param pos position in buffer
265  * @param buf the buffer to write data to
266  * @param max max number of bytes available in buffer
267  * @return bytes written to buffer
268  */
269 static ssize_t
270 mhd_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
271 {
272   struct Session *s = cls;
273   struct HTTP_Message *msg;
274   int bytes_read = 0;
275
276   msg = s->msg_head;
277   if (msg != NULL)
278   {
279     /* sending */
280     if ((msg->size - msg->pos) <= max)
281     {
282       memcpy (buf, &msg->buf[msg->pos], (msg->size - msg->pos));
283       bytes_read = msg->size - msg->pos;
284       msg->pos += (msg->size - msg->pos);
285     }
286     else
287     {
288       memcpy (buf, &msg->buf[msg->pos], max);
289       msg->pos += max;
290       bytes_read = max;
291     }
292
293     /* removing message */
294     if (msg->pos == msg->size)
295     {
296       if (NULL != msg->transmit_cont)
297         msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
298       GNUNET_CONTAINER_DLL_remove(s->msg_head, s->msg_tail, msg);
299       GNUNET_free (msg);
300     }
301   }
302 #if DEBUG_CONNECTIONS
303   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection %X: MHD has sent %u bytes\n",
304               s, bytes_read);
305 #endif
306   return bytes_read;
307 }
308
309 /**
310  * Process GET or PUT request received via MHD.  For
311  * GET, queue response that will send back our pending
312  * messages.  For PUT, process incoming data and send
313  * to GNUnet core.  In either case, check if a session
314  * already exists and create a new one if not.
315  */
316 static int
317 server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
318                   const char *url, const char *method, const char *version,
319                   const char *upload_data, size_t * upload_data_size,
320                   void **httpSessionCache)
321 {
322   struct Plugin *plugin = cls;
323   struct ServerConnection *sc = *httpSessionCache;
324   struct Session *s = NULL;
325
326   int res = MHD_YES;
327   struct MHD_Response *response;
328
329   GNUNET_assert (cls != NULL);
330   /* new connection */
331   if (sc == NULL)
332     {
333     uint32_t tag;
334     const union MHD_ConnectionInfo *conn_info;
335     size_t addrlen;
336     struct GNUNET_PeerIdentity target;
337     int check = GNUNET_NO;
338     struct Session * t;
339     int direction;
340
341     conn_info = MHD_get_connection_info (mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
342     if (conn_info->client_addr->sa_family == AF_INET)
343       addrlen = sizeof (struct sockaddr_in);
344     else if (conn_info->client_addr->sa_family == AF_INET6)
345       addrlen = sizeof (struct sockaddr_in6);
346     else
347       return MHD_NO;
348
349     if ((strlen(&url[1]) >= 105)  && (url[104] == ';'))
350     {
351       char hash[104];
352       char * tagc = (char *) &url[105];
353       memcpy(&hash, &url[1], 103);
354       hash [103] = '\0';
355       if (GNUNET_OK == GNUNET_CRYPTO_hash_from_string ((const char *) &hash, &(target.hashPubKey)))
356       {
357         tag = strtoul (tagc, NULL, 10);
358         if (tagc > 0)
359           check = GNUNET_YES;
360       }
361     }
362
363     if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
364       direction = _RECEIVE;
365     if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
366       direction = _SEND;
367
368     if (check == GNUNET_NO)
369       goto error;
370
371     plugin->cur_connections++;
372
373 #if VERBOSE_SERVER
374     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "server: New inbound connection from %s with tag %u\n", GNUNET_h2s_full(&(target.hashPubKey)), tag);
375 #endif
376     /* find duplicate session */
377
378     t = plugin->head;
379
380     while (t != NULL)
381     {
382       if ((t->inbound) && (0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) &&
383           /* FIXME add source address comparison */
384           (t->tag == tag))
385       break;
386       t = t->next;
387     }
388     if (t != NULL)
389     {
390 #if VERBOSE_SERVER
391       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "server: Duplicate session, dismissing new connection from peer `%s'\n", GNUNET_i2s (&target));
392 #endif
393       goto error;
394     }
395
396     /* find semi-session */
397     t = plugin->server_semi_head;
398
399     while (t != NULL)
400     {
401       /* FIXME add source address comparison */
402       if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) &&
403           (t->tag == tag))
404       {
405         break;
406       }
407       t = t->next;
408     }
409
410     if (t == NULL)
411       goto create;
412
413 #if VERBOSE_SERVER
414       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "server: Found existing semi-session for `%s'\n", GNUNET_i2s (&target));
415 #endif
416
417     if ((direction == _SEND) && (t->server_send != NULL))
418     {
419 #if VERBOSE_SERVER
420       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "server: Duplicate GET session, dismissing new connection from peer `%s'\n", GNUNET_i2s (&target));
421 #endif
422       goto error;
423     }
424     else
425     {
426       s = t;
427       GNUNET_CONTAINER_DLL_remove(plugin->server_semi_head, plugin->server_semi_tail, s);
428       GNUNET_CONTAINER_DLL_insert(plugin->head, plugin->tail, s);
429 #if VERBOSE_SERVER
430       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "server: Found matching semi-session, merging session for peer `%s'\n", GNUNET_i2s (&target));
431 #endif
432
433       goto found;
434     }
435     if ((direction == _RECEIVE) && (t->server_recv != NULL))
436     {
437 #if VERBOSE_SERVER
438       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "server: Duplicate PUT session, dismissing new connection from peer `%s'\n", GNUNET_i2s (&target));
439 #endif
440       goto error;
441     }
442     else
443     {
444       s = t;
445       GNUNET_CONTAINER_DLL_remove(plugin->server_semi_head, plugin->server_semi_tail, s);
446       GNUNET_CONTAINER_DLL_insert(plugin->head, plugin->tail, s);
447 #if VERBOSE_SERVER
448       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "server: Found matching semi-session, merging session for peer `%s'\n", GNUNET_i2s (&target));
449 #endif
450       goto found;
451     }
452
453 create:
454 /* create new session */
455 #if VERBOSE_SERVER
456       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "server: Creating new session for peer `%s' \n", GNUNET_i2s (&target));
457 #endif
458
459     s = create_session(plugin,
460                         &target,
461                         conn_info->client_addr,
462                         addrlen,
463                         NULL,
464                         NULL);
465
466     s->inbound = GNUNET_YES;
467     s->tag= tag;
468     if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
469       s->server_recv = s;
470     if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
471       s->server_send = s;
472     GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head, plugin->server_semi_tail, s);
473
474     goto found;
475 error:
476         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "server: Invalid connection request\n");
477         response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
478         res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
479         MHD_destroy_response (response);
480         return res;
481 found:
482     sc = GNUNET_malloc (sizeof (struct ServerConnection));
483     sc->mhd_conn = mhd_connection;
484     sc->direction = direction;
485     sc->session = s;
486     if (direction == _SEND)
487       s->server_send = sc;
488     if (direction == _RECEIVE)
489       s->server_recv = sc;
490
491     (*httpSessionCache) = sc;
492   }
493
494
495   /* existing connection */
496   sc = (*httpSessionCache);
497   s = sc->session;
498
499   /* connection is to be disconnected*/
500   if (sc->disconnect == GNUNET_YES)
501   {
502     response = MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!", MHD_NO, MHD_NO);
503     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
504 #if VERBOSE_SERVER
505     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
506                 "Sent HTTP/1.1: 200 OK as PUT Response\n");
507 #endif
508     MHD_destroy_response (response);
509     return MHD_YES;
510   }
511
512   GNUNET_assert (s != NULL);
513   if (sc->direction == _SEND)
514   {
515     response =
516         MHD_create_response_from_callback (-1, 32 * 1024, &mhd_send_callback,
517                                            s, NULL);
518     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
519     MHD_destroy_response (response);
520     return MHD_YES;
521   }
522   if (sc->direction == _RECEIVE)
523   {
524     if (*upload_data_size == 0)
525     {
526 #if VERBOSE_SERVER
527   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
528                    "Server: peer `%s' PUT on address `%s' connected\n",
529                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
530 #endif
531       return MHD_YES;
532     }
533
534     /* Recieving data */
535     if ((*upload_data_size > 0))
536     {
537 #if VERBOSE_SERVER
538   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
539                    "Server: peer `%s' PUT on address `%s' received %u bytes\n",
540                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
541 #endif
542       if ((GNUNET_TIME_absolute_get().abs_value < s->delay.abs_value))
543       {
544         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
545                     "Connection %X: PUT with %u bytes forwarded to MST\n", s,
546                     *upload_data_size);
547
548         if (s->msg_tk == NULL)
549         {
550           s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s);
551         }
552         res = GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, *upload_data_size, GNUNET_NO, GNUNET_NO);
553         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
554                     "Server: Received %u bytes\n",
555                     *upload_data_size);
556         (*upload_data_size) = 0;
557       }
558       else
559       {
560 /*
561 #if DEBUG_HTTP
562         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
563                     "Connection %X: no inbound bandwidth available! Next read was delayed for  %llu ms\n",
564                     s, ps->peercontext->delay.rel_value);
565 #endif
566 */
567       }
568       return MHD_YES;
569     }
570     else
571       return MHD_NO;
572   }
573   return res;
574 }
575
576 static void
577 server_disconnect_cb (void *cls, struct MHD_Connection *connection,
578                       void **httpSessionCache)
579 {
580   struct ServerConnection *sc = *httpSessionCache;
581   struct ServerConnection *tc = *httpSessionCache;
582   struct Session * s = NULL;
583   struct Session * t = NULL;
584   struct Plugin * plugin = NULL;
585
586   if (sc == NULL)
587     return;
588
589   s = sc->session;
590   plugin = s-> plugin;
591   if (sc->direction == _SEND)
592   {
593 #if VERBOSE_SERVER
594   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
595                    "Server: peer `%s' PUT on address `%s' disconnected\n",
596                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
597 #endif
598     s->server_send = NULL;
599     /* FIXME miminimize timeout here */
600     if (s->server_recv != NULL)
601     {
602       tc = s->server_recv;
603       tc->disconnect = GNUNET_YES;
604     }
605   }
606   if (sc->direction == _RECEIVE)
607   {
608 #if VERBOSE_SERVER
609   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
610                    "Server: peer `%s' GET on address `%s' disconnected\n",
611                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
612 #endif
613     s->server_recv = NULL;
614     //MHD_
615     if (s->server_send != NULL)
616     {
617       tc = s->server_send;
618       tc->disconnect = GNUNET_YES;
619     }
620     if (s->msg_tk != NULL)
621        GNUNET_SERVER_mst_destroy(s->msg_tk);
622   }
623   GNUNET_free (sc);
624
625   t = plugin->server_semi_head;
626   while (t != NULL)
627   {
628     if (t == s)
629     {
630       GNUNET_CONTAINER_DLL_remove(plugin->server_semi_head, plugin->server_semi_tail, s);
631       GNUNET_CONTAINER_DLL_insert(plugin->head, plugin->tail, s);
632       break;
633     }
634     t = t->next;
635   }
636   plugin->cur_connections--;
637
638
639   if ((s->server_send == NULL) && (s->server_recv == NULL))
640   {
641 #if VERBOSE_SERVER
642   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
643                    "Server: peer `%s' on address `%s' disconnected\n",
644                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
645 #endif
646
647     notify_session_end(s->plugin, &s->target, s);
648   }
649 }
650
651 int
652 server_disconnect (struct Session *s)
653 {
654   struct Plugin *plugin = s->plugin;
655   struct Session *t = plugin->head;
656
657   while (t != NULL)
658   {
659     if (t->inbound == GNUNET_YES)
660     {
661       if (t->server_send != NULL)
662       {
663         ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES;
664       }
665       if (t->server_send != NULL)
666       {
667         ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES;
668       }
669     }
670     t = t->next;
671   }
672
673
674   return GNUNET_OK;
675 }
676
677 int
678 server_send (struct Session *s, struct HTTP_Message * msg)
679 {
680   GNUNET_CONTAINER_DLL_insert (s->msg_head, s->msg_tail, msg);
681   return GNUNET_OK;
682 }
683
684 /**
685  * Function that queries MHD's select sets and
686  * starts the task waiting for them.
687  * @param plugin plugin
688  * @param daemon_handle the MHD daemon handle
689  * @return gnunet task identifier
690  */
691 static GNUNET_SCHEDULER_TaskIdentifier
692 server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle);
693
694 /**
695  * Call MHD IPv4 to process pending requests and then go back
696  * and schedule the next run.
697  * @param cls plugin as closure
698  * @param tc task context
699  */
700 static void
701 server_v4_run (void *cls,
702                            const struct GNUNET_SCHEDULER_TaskContext *tc)
703 {
704   struct Plugin *plugin = cls;
705   GNUNET_assert (cls != NULL);
706
707   plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
708
709   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
710     return;
711
712   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4));
713   plugin->server_v4_task = server_schedule (plugin, plugin->server_v4);
714 }
715
716
717 /**
718  * Call MHD IPv6 to process pending requests and then go back
719  * and schedule the next run.
720  * @param cls plugin as closure
721  * @param tc task context
722  */
723 static void
724 server_v6_run (void *cls,
725                            const struct GNUNET_SCHEDULER_TaskContext *tc)
726 {
727   struct Plugin *plugin = cls;
728   GNUNET_assert (cls != NULL);
729
730   plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
731
732   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
733     return;
734
735   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6));
736   plugin->server_v6_task = server_schedule (plugin, plugin->server_v6);
737 }
738
739 /**
740  * Function that queries MHD's select sets and
741  * starts the task waiting for them.
742  * @param plugin plugin
743  * @param daemon_handle the MHD daemon handle
744  * @return gnunet task identifier
745  */
746 static GNUNET_SCHEDULER_TaskIdentifier
747 server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle)
748 {
749   GNUNET_SCHEDULER_TaskIdentifier ret;
750   fd_set rs;
751   fd_set ws;
752   fd_set es;
753   struct GNUNET_NETWORK_FDSet *wrs;
754   struct GNUNET_NETWORK_FDSet *wws;
755   struct GNUNET_NETWORK_FDSet *wes;
756   int max;
757   unsigned long long timeout;
758   int haveto;
759   struct GNUNET_TIME_Relative tv;
760
761   ret = GNUNET_SCHEDULER_NO_TASK;
762   FD_ZERO (&rs);
763   FD_ZERO (&ws);
764   FD_ZERO (&es);
765   wrs = GNUNET_NETWORK_fdset_create ();
766   wes = GNUNET_NETWORK_fdset_create ();
767   wws = GNUNET_NETWORK_fdset_create ();
768   max = -1;
769   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
770   haveto = MHD_get_timeout (daemon_handle, &timeout);
771   if (haveto == MHD_YES)
772     tv.rel_value = (uint64_t) timeout;
773   else
774     tv = GNUNET_TIME_UNIT_SECONDS;
775   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
776   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
777   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
778   if (daemon_handle == plugin->server_v4)
779   {
780     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
781     {
782       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
783       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
784     }
785
786     ret =
787         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
788                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
789                                      &server_v4_run, plugin);
790   }
791   if (daemon_handle == plugin->server_v6)
792   {
793     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
794     {
795       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
796       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
797     }
798
799     ret =
800         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
801                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
802                                      &server_v6_run, plugin);
803   }
804   GNUNET_NETWORK_fdset_destroy (wrs);
805   GNUNET_NETWORK_fdset_destroy (wws);
806   GNUNET_NETWORK_fdset_destroy (wes);
807   return ret;
808 }
809
810 int
811 server_start (struct Plugin *plugin)
812 {
813   int res = GNUNET_OK;
814
815 #if BUILD_HTTPS
816   res = server_load_certificate (plugin);
817   if (res == GNUNET_SYSERR)
818   {
819     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TABORT\n");
820     return res;
821   }
822 #endif
823
824   plugin->server_v4 = NULL;
825   if (plugin->ipv4 == GNUNET_YES)
826   {
827     plugin->server_v4 = MHD_start_daemon (
828 #if VERBOSE_SERVER
829                                            MHD_USE_DEBUG |
830 #endif
831 #if BUILD_HTTPS
832                                            MHD_USE_SSL |
833 #endif
834                                            MHD_NO_FLAG, plugin->port,
835                                            &server_accept_cb, plugin,
836                                            &server_access_cb, plugin,
837                                            //MHD_OPTION_SOCK_ADDR,
838                                            //(struct sockaddr_in *)
839                                            //plugin->bind4_address,
840                                            MHD_OPTION_CONNECTION_LIMIT,
841                                            (unsigned int)
842                                            plugin->max_connections,
843 #if BUILD_HTTPS
844                                            MHD_OPTION_HTTPS_PRIORITIES,
845                                            plugin->crypto_init,
846                                            MHD_OPTION_HTTPS_MEM_KEY,
847                                            plugin->key,
848                                            MHD_OPTION_HTTPS_MEM_CERT,
849                                            plugin->cert,
850 #endif
851                                            MHD_OPTION_CONNECTION_TIMEOUT,
852                                            (unsigned int) 3,
853                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
854                                            (size_t) (2 *
855                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
856                                            MHD_OPTION_NOTIFY_COMPLETED,
857                                            &server_disconnect_cb, plugin,
858                                            MHD_OPTION_EXTERNAL_LOGGER,
859                                            server_log, NULL, MHD_OPTION_END);
860     if (plugin->server_v4 == NULL)
861       res = GNUNET_SYSERR;
862   }
863   plugin->server_v6 = NULL;
864   if (plugin->ipv6 == GNUNET_YES)
865   {
866     plugin->server_v6 = MHD_start_daemon (
867 #if VERBOSE_SERVER
868                                            MHD_USE_DEBUG |
869 #endif
870 #if BUILD_HTTPS
871                                            MHD_USE_SSL |
872 #endif
873                                            MHD_USE_IPv6, plugin->port,
874                                            &server_accept_cb, plugin,
875                                            &server_access_cb, plugin,
876                                            //MHD_OPTION_SOCK_ADDR,
877                                            //tmp,
878                                            MHD_OPTION_CONNECTION_LIMIT,
879                                            (unsigned int)
880                                            plugin->max_connections,
881 #if BUILD_HTTPS
882                                            MHD_OPTION_HTTPS_PRIORITIES,
883                                            plugin->crypto_init,
884                                            MHD_OPTION_HTTPS_MEM_KEY,
885                                            plugin->key,
886                                            MHD_OPTION_HTTPS_MEM_CERT,
887                                            plugin->cert,
888 #endif
889                                            MHD_OPTION_CONNECTION_TIMEOUT,
890                                            (unsigned int) 3,
891                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
892                                            (size_t) (2 *
893                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
894                                            MHD_OPTION_NOTIFY_COMPLETED,
895                                            &server_disconnect_cb, plugin,
896                                            MHD_OPTION_EXTERNAL_LOGGER,
897                                            server_log, NULL, MHD_OPTION_END);
898
899     if (plugin->server_v6 == NULL)
900       res = GNUNET_SYSERR;
901   }
902
903   if (plugin->server_v4 != NULL)
904     plugin->server_v4_task = server_schedule (plugin, plugin->server_v4);
905   if (plugin->server_v6 != NULL)
906     plugin->server_v6_task = server_schedule (plugin, plugin->server_v6);
907
908 #if DEBUG_HTTP
909   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
910                    "%s server component started on port %u\n", plugin->name,
911                    plugin->port);
912 #endif
913   return res;
914 }
915
916 void
917 server_stop (struct Plugin *plugin)
918 {
919   struct Session *s = NULL;
920   struct Session *t = NULL;
921
922   if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
923   {
924     GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
925     plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
926   }
927
928   if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
929   {
930     GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
931     plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
932   }
933
934   if (plugin->server_v4 != NULL)
935   {
936     MHD_stop_daemon (plugin->server_v4);
937     plugin->server_v4 = NULL;
938   }
939   if (plugin->server_v6 != NULL)
940   {
941     MHD_stop_daemon (plugin->server_v6);
942     plugin->server_v6 = NULL;
943   }
944
945   /* cleaning up semi-sessions never propagated */
946   s = plugin->server_semi_head;
947   while (s != NULL)
948   {
949     t = s->next;
950     if (s->msg_tk != NULL)
951        GNUNET_SERVER_mst_destroy(s->msg_tk);
952     delete_session (s);
953     s = t;
954   }
955
956 #if BUILD_HTTPS
957   GNUNET_free_non_null (plugin->crypto_init);
958   GNUNET_free_non_null (plugin->cert);
959   GNUNET_free_non_null (plugin->key);
960 #endif
961
962 #if DEBUG_HTTP
963   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
964                    "%s server component stopped\n", plugin->name);
965 #endif
966 }
967
968
969
970 /* end of plugin_transport_http.c */