more code
[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_server.c
23  * @brief HTTP/S server transport plugin
24  * @author Matthias Wachs
25  */
26
27 #include "platform.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_connection_lib.h"
30 #include "gnunet_server_lib.h"
31 #include "gnunet_service_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_transport_plugin.h"
35
36 #include "gnunet_container_lib.h"
37 #include "gnunet_nat_lib.h"
38 #include "plugin_transport_http_common.h"
39 #include "microhttpd.h"
40
41 #if BUILD_HTTPS
42 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_server_init
43 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_server_done
44 #else
45 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_server_init
46 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_server_done
47 #endif
48
49 #define HTTP_NOT_VALIDATED_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
50
51 #define TESTING GNUNET_NO
52
53 #if TESTING
54 #define TIMEOUT_LOG GNUNET_ERROR_TYPE_ERROR
55 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
56 #else
57 #define TIMEOUT_LOG GNUNET_ERROR_TYPE_DEBUG
58 #define TIMEOUT GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT
59 #endif
60
61 #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>"
62 #define _RECEIVE 0
63 #define _SEND 1
64
65 /**
66  * Encapsulation of all of the state of the plugin.
67  */
68 struct Plugin;
69
70
71 /**
72  * Session handle for connections.
73  */
74 struct HttpServerSession
75 {
76   /**
77    * Stored in a linked list.
78    */
79   struct HttpServerSession *next;
80
81   /**
82    * Stored in a linked list.
83    */
84   struct HttpServerSession *prev;
85
86   /**
87    * To whom are we talking to (set to our identity
88    * if we are still waiting for the welcome message)
89    */
90   struct GNUNET_PeerIdentity target;
91
92   /**
93    * Pointer to the global plugin struct.
94    */
95   struct HTTP_Server_Plugin *plugin;
96
97   /**
98    * next pointer for double linked list
99    */
100   struct HTTP_Message *msg_head;
101
102   /**
103    * previous pointer for double linked list
104    */
105   struct HTTP_Message *msg_tail;
106
107   /**
108    * Message stream tokenizer for incoming data
109    */
110   struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk;
111
112   /**
113    * Client send handle
114    */
115   struct ServerConnection *server_recv;
116
117   /**
118    * Client send handle
119    */
120   struct ServerConnection *server_send;
121
122   /**
123    * Address
124    */
125   void *addr;
126
127   size_t addrlen;
128
129   /**
130    * Inbound or outbound connection
131    * Outbound: GNUNET_NO (client is used to send and receive)
132    * Inbound : GNUNET_YES (server is used to send and receive)
133    */
134   int inbound;
135
136   /**
137    * Unique HTTP/S connection tag for this connection
138    */
139   uint32_t tag;
140
141   /**
142    * ATS network type in NBO
143    */
144   uint32_t ats_address_network_type;
145
146   /**
147    * Absolute time when to receive data again
148    * Used for receive throttling
149    */
150   struct GNUNET_TIME_Absolute next_receive;
151
152   /**
153    * Session timeout task
154    */
155   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
156 };
157
158 struct ServerConnection
159 {
160   /* _RECV or _SEND */
161   int direction;
162
163   /* Should this connection get disconnected? GNUNET_YES/NO  */
164   int disconnect;
165
166   /* The session this server connection belongs to */
167   struct HttpServerSession *session;
168
169   /* The MHD connection */
170   struct MHD_Connection *mhd_conn;
171 };
172
173 /**
174  * Encapsulation of all of the state of the plugin.
175  */
176 struct HTTP_Server_Plugin
177 {
178   /**
179    * Our environment.
180    */
181   struct GNUNET_TRANSPORT_PluginEnvironment *env;
182
183   /**
184    * Linked list head of open sessions.
185    */
186
187   struct HttpServerSession *head;
188
189   /**
190    * Linked list tail of open sessions.
191    */
192   struct HttpServerSession *tail;
193
194   /**
195    * Plugin name
196    */
197   char *name;
198
199   /**
200    * Protocol
201    */
202   char *protocol;
203
204   /**
205    * External address
206    */
207   char *external_hostname;
208
209   /**
210    * Maximum number of sockets the plugin can use
211    * Each http inbound /outbound connections are two connections
212    */
213   unsigned int max_connections;
214
215   /**
216    * Current number of sockets the plugin can use
217    * Each http inbound /outbound connections are two connections
218    */
219   unsigned int cur_connections;
220
221   /**
222    * External hostname the plugin can be connected to, can be different to
223    * the host's FQDN, used e.g. for reverse proxying
224    */
225   char *ext_addr;
226
227   /**
228    * External address length
229    */
230   size_t ext_addr_len;
231
232   /**
233    * use IPv6
234    */
235   uint16_t use_ipv6;
236
237   /**
238    * use IPv4
239    */
240   uint16_t use_ipv4;
241
242   /**
243    * Port used
244    */
245   uint16_t port;
246
247   /**
248    * Task calling transport service about external address
249    */
250   GNUNET_SCHEDULER_TaskIdentifier notify_ext_task;
251
252   /**
253    * NAT handle & address management
254    */
255   struct GNUNET_NAT_Handle *nat;
256
257   /**
258    * Server semi connections
259    * A full session consists of 2 semi-connections: send and receive
260    * If not both directions are established the server keeps this sessions here
261    */
262   struct HttpServerSession *server_semi_head;
263
264   struct HttpServerSession *server_semi_tail;
265
266   /**
267    * List of own addresses
268    */
269
270   /**
271    * IPv4 addresses DLL head
272    */
273   struct HttpAddressWrapper *addr_head;
274
275   /**
276    * IPv4 addresses DLL tail
277    */
278   struct HttpAddressWrapper *addr_tail;
279
280   /**
281    * IPv4 server socket to bind to
282    */
283   struct sockaddr_in *server_addr_v4;
284
285   /**
286    * IPv6 server socket to bind to
287    */
288   struct sockaddr_in6 *server_addr_v6;
289
290   /**
291    * MHD IPv4 task
292    */
293   GNUNET_SCHEDULER_TaskIdentifier server_v4_task;
294
295   /**
296    * MHD IPv6 task
297    */
298   GNUNET_SCHEDULER_TaskIdentifier server_v6_task;
299
300   /**
301    * The IPv4 server is scheduled to run asap
302    */
303   int server_v4_immediately;
304
305   /**
306    * The IPv6 server is scheduled to run asap
307    */
308   int server_v6_immediately;
309
310   /**
311    * MHD IPv4 daemon
312    */
313   struct MHD_Daemon *server_v4;
314
315   /**
316    * MHD IPv4 daemon
317    */
318   struct MHD_Daemon *server_v6;
319
320 #if BUILD_HTTPS
321   /**
322    * Crypto related
323    *
324    * Example:
325    *
326    * Use RC4-128 instead of AES:
327    * NONE:+VERS-TLS1.0:+ARCFOUR-128:+SHA1:+RSA:+COMP-NULL
328    *
329    */
330   char *crypto_init;
331
332   /**
333    * TLS key
334    */
335   char *key;
336
337   /**
338    * TLS certificate
339    */
340   char *cert;
341 #endif
342
343 };
344
345 /**
346  * Wrapper to manage addresses
347  */
348 struct HttpAddressWrapper
349 {
350   /**
351    * Linked list next
352    */
353   struct HttpAddressWrapper *next;
354
355   /**
356    * Linked list previous
357    */
358   struct HttpAddressWrapper *prev;
359
360   void *addr;
361
362   size_t addrlen;
363 };
364
365 /**
366  *  Message to send using http
367  */
368 struct HTTP_Message
369 {
370   /**
371    * next pointer for double linked list
372    */
373   struct HTTP_Message *next;
374
375   /**
376    * previous pointer for double linked list
377    */
378   struct HTTP_Message *prev;
379
380   /**
381    * buffer containing data to send
382    */
383   char *buf;
384
385   /**
386    * amount of data already sent
387    */
388   size_t pos;
389
390   /**
391    * buffer length
392    */
393   size_t size;
394
395   /**
396    * Continuation function to call once the transmission buffer
397    * has again space available.  NULL if there is no
398    * continuation to call.
399    */
400   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
401
402   /**
403    * Closure for transmit_cont.
404    */
405   void *transmit_cont_cls;
406 };
407
408
409 static struct Plugin * p;
410
411 /**
412  * Start session timeout
413  */
414 static void
415 server_start_session_timeout (struct HttpServerSession *s);
416
417 #if 0
418 /**
419  * Increment session timeout due to activity
420  */
421 static void
422 server_reschedule_session_timeout (struct HttpServerSession *s);
423 #endif
424 /**
425  * Cancel timeout
426  */
427 static void
428 server_stop_session_timeout (struct HttpServerSession *s);
429
430 /**
431  * Function that can be used by the transport service to transmit
432  * a message using the plugin.   Note that in the case of a
433  * peer disconnecting, the continuation MUST be called
434  * prior to the disconnect notification itself.  This function
435  * will be called with this peer's HELLO message to initiate
436  * a fresh connection to another peer.
437  *
438  * @param cls closure
439  * @param session which session must be used
440  * @param msgbuf the message to transmit
441  * @param msgbuf_size number of bytes in 'msgbuf'
442  * @param priority how important is the message (most plugins will
443  *                 ignore message priority and just FIFO)
444  * @param to how long to wait at most for the transmission (does not
445  *                require plugins to discard the message after the timeout,
446  *                just advisory for the desired delay; most plugins will ignore
447  *                this as well)
448  * @param cont continuation to call once the message has
449  *        been transmitted (or if the transport is ready
450  *        for the next transmission call; or if the
451  *        peer disconnected...); can be NULL
452  * @param cont_cls closure for cont
453  * @return number of bytes used (on the physical network, with overheads);
454  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
455  *         and does NOT mean that the message was not transmitted (DV)
456  */
457 static ssize_t
458 http_server_plugin_send (void *cls,
459                   struct Session *session,
460                   const char *msgbuf, size_t msgbuf_size,
461                   unsigned int priority,
462                   struct GNUNET_TIME_Relative to,
463                   GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
464 {
465   struct HTTP_Server_Plugin *plugin = cls;
466   int bytes_sent = 0;
467
468   GNUNET_assert (plugin != NULL);
469   GNUNET_assert (session != NULL);
470
471   GNUNET_break (0);
472
473   /*  struct Plugin *plugin = cls; */
474   return bytes_sent;
475 }
476
477
478
479 /**
480  * Function that can be used to force the plugin to disconnect
481  * from the given peer and cancel all previous transmissions
482  * (and their continuationc).
483  *
484  * @param cls closure
485  * @param target peer from which to disconnect
486  */
487 static void
488 http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
489 {
490   // struct Plugin *plugin = cls;
491   GNUNET_break (0);
492 }
493
494
495 /**
496  * Another peer has suggested an address for this
497  * peer and transport plugin.  Check that this could be a valid
498  * address.  If so, consider adding it to the list
499  * of addresses.
500  *
501  * @param cls closure
502  * @param addr pointer to the address
503  * @param addrlen length of addr
504  * @return GNUNET_OK if this is a plausible address for this peer
505  *         and transport
506  */
507 static int
508 http_server_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
509 {
510   struct HTTP_Server_Plugin *plugin = cls;
511   struct HttpAddressWrapper *w = plugin->addr_head;
512
513   if ((NULL != plugin->ext_addr) && GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, plugin->ext_addr, plugin->ext_addr_len)))
514     return GNUNET_OK;
515
516   while (NULL != w)
517   {
518     if (GNUNET_YES == (http_common_cmp_addresses(addr,
519                                                  addrlen,
520                                                  w->addr,
521                                                  w->addrlen)))
522       return GNUNET_OK;
523   }
524
525   return GNUNET_NO;
526 }
527
528 /**
529  * Creates a new outbound session the transport
530  * service will use to send data to the peer
531  *
532  * Since HTTP/S server cannot create sessions, always return NULL
533  *
534  * @param cls the plugin
535  * @param address the address
536  * @return always NULL
537  */
538 static struct Session *
539 http_server_plugin_get_session (void *cls,
540                                 const struct GNUNET_HELLO_Address *address)
541 {
542   return NULL;
543 }
544
545
546 /**
547  * Deleting the session
548  * Must not be used afterwards
549  */
550
551 void
552 server_delete_session (struct HttpServerSession *s)
553 {
554   struct HTTP_Server_Plugin *plugin = s->plugin;
555   server_stop_session_timeout(s);
556
557   GNUNET_CONTAINER_DLL_remove (plugin->head, plugin->tail, s);
558   struct HTTP_Message *msg = s->msg_head;
559   struct HTTP_Message *tmp = NULL;
560
561   while (msg != NULL)
562   {
563     tmp = msg->next;
564
565     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
566     if (msg->transmit_cont != NULL)
567     {
568       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
569     }
570     GNUNET_free (msg);
571     msg = tmp;
572   }
573
574   if (s->msg_tk != NULL)
575   {
576     GNUNET_SERVER_mst_destroy (s->msg_tk);
577     s->msg_tk = NULL;
578   }
579   GNUNET_free (s->addr);
580   GNUNET_free_non_null (s->server_recv);
581   GNUNET_free_non_null (s->server_send);
582   GNUNET_free (s);
583 }
584
585 int
586 server_disconnect (struct HttpServerSession *s)
587 {
588   struct ServerConnection * send;
589   struct ServerConnection * recv;
590
591   send = (struct ServerConnection *) s->server_send;
592   if (s->server_send != NULL)
593   {
594     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
595                      "Server: %p / %p Terminating inbound PUT session to peer `%s'\n",
596                      s, s->server_send, GNUNET_i2s (&s->target));
597
598     send->disconnect = GNUNET_YES;
599 #if MHD_VERSION >= 0x00090E00
600       MHD_set_connection_option (send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
601                                  1);
602 #endif
603   }
604
605   recv = (struct ServerConnection *) s->server_recv;
606   if (recv != NULL)
607   {
608     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
609                      "Server: %p / %p Terminating inbound GET session to peer `%s'\n",
610                      s, s->server_recv, GNUNET_i2s (&s->target));
611
612     recv->disconnect = GNUNET_YES;
613 #if MHD_VERSION >= 0x00090E00
614       MHD_set_connection_option (recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
615                                  1);
616 #endif
617   }
618
619   /* Schedule connection immediately */
620 #if 0
621   if (s->addrlen == sizeof (struct IPv4HttpAddress))
622   {
623     server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES);
624   }
625   else if (s->addrlen == sizeof (struct IPv6HttpAddress))
626   {
627     server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES);
628   }
629 #endif
630   return GNUNET_OK;
631
632 }
633
634
635 /**
636 * Cancel timeout
637 */
638 static void
639 server_stop_session_timeout (struct HttpServerSession *s)
640 {
641  GNUNET_assert (NULL != s);
642
643  if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task)
644  {
645    GNUNET_SCHEDULER_cancel (s->timeout_task);
646    s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
647    GNUNET_log (TIMEOUT_LOG, "Timeout stopped for session %p\n", s);
648  }
649 }
650
651 /**
652  * Function that queries MHD's select sets and
653  * starts the task waiting for them.
654  * @param plugin plugin
655  * @param daemon_handle the MHD daemon handle
656  * @return gnunet task identifier
657  */
658 static GNUNET_SCHEDULER_TaskIdentifier
659 server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_handle,
660                  int now);
661
662 /**
663  * Reschedule the execution of both IPv4 and IPv6 server
664  * @param plugin the plugin
665  * @param server which server to schedule v4 or v6?
666  * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait
667  * until timeout
668  */
669 static void
670 server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, int now)
671 {
672   if ((server == plugin->server_v4) && (plugin->server_v4 != NULL))
673   {
674     if (GNUNET_YES == plugin->server_v4_immediately)
675       return; /* No rescheduling, server will run asap */
676
677     if (GNUNET_YES == now)
678       plugin->server_v4_immediately = GNUNET_YES;
679
680     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
681     {
682       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
683       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
684     }
685     plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now);
686   }
687
688   if ((server == plugin->server_v6) && (plugin->server_v6 != NULL))
689   {
690     if (GNUNET_YES == plugin->server_v6_immediately)
691       return; /* No rescheduling, server will run asap */
692
693     if (GNUNET_YES == now)
694       plugin->server_v6_immediately = GNUNET_YES;
695
696     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
697     {
698       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
699       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
700     }
701     plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now);
702   }
703 }
704
705
706 static struct ServerConnection *
707 server_lookup_connection (struct HTTP_Server_Plugin *plugin,
708                        struct MHD_Connection *mhd_connection, const char *url,
709                        const char *method)
710 {
711   struct HttpServerSession *s = NULL;
712   struct HttpServerSession *t;
713   struct ServerConnection *sc = NULL;
714   const union MHD_ConnectionInfo *conn_info;
715   struct GNUNET_ATS_Information ats;
716
717   char *addr;
718   size_t addr_len;
719
720   struct GNUNET_PeerIdentity target;
721   uint32_t tag = 0;
722   int direction = GNUNET_SYSERR;
723
724   /* url parsing variables */
725   size_t url_len;
726   char *url_end;
727   char *hash_start;
728   char *hash_end;
729   char *tag_start;
730   char *tag_end;
731
732   conn_info = MHD_get_connection_info (mhd_connection,
733                                        MHD_CONNECTION_INFO_CLIENT_ADDRESS);
734   if ((conn_info->client_addr->sa_family != AF_INET) &&
735       (conn_info->client_addr->sa_family != AF_INET6))
736     return NULL;
737
738   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
739                    "New %s connection from %s\n", method, url);
740   /* URL parsing
741    * URL is valid if it is in the form [peerid[103];tag]*/
742   url_len = strlen (url);
743   url_end = (char *) &url[url_len];
744
745   if (url_len < 105)
746   {
747     goto error; /* too short */
748   }
749   hash_start = strrchr (url, '/');
750   if (NULL == hash_start)
751   {
752     goto error; /* '/' delimiter not found */
753   }
754   if (hash_start >= url_end)
755   {
756     goto error; /* mal formed */
757   }
758   hash_start++;
759
760   hash_end = strrchr (hash_start, ';');
761   if (NULL == hash_end)
762     goto error; /* ';' delimiter not found */
763   if (hash_end >= url_end)
764   {
765     goto error; /* mal formed */
766   }
767
768   if (hash_start >= hash_end)
769   {
770     goto error; /* mal formed */
771   }
772
773   if ((strlen(hash_start) - strlen(hash_end)) != 103)
774   {
775     goto error; /* invalid hash length */
776   }
777
778   char hash[104];
779   memcpy (hash, hash_start, 103);
780   hash[103] = '\0';
781   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target.hashPubKey)))
782   {
783     goto error; /* mal formed */
784   }
785
786   if (hash_end >= url_end)
787   {
788     goto error; /* mal formed */
789   }
790
791   tag_start = &hash_end[1];
792   /* Converting tag */
793   tag_end = NULL;
794   tag = strtoul (tag_start, &tag_end, 10);
795   if (tag == 0)
796   {
797     goto error; /* mal formed */
798   }
799   if (tag_end == NULL)
800   {
801     goto error; /* mal formed */
802   }
803   if (tag_end != url_end)
804   {
805     goto error; /* mal formed */
806   }
807
808   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
809     direction = _RECEIVE;
810   else if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
811     direction = _SEND;
812   else
813   {
814     goto error;
815   }
816
817   plugin->cur_connections++;
818   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
819                    "New %s connection from %s with tag %u (%u of %u)\n",
820                    method,
821                    GNUNET_i2s (&target), tag,
822                    plugin->cur_connections, plugin->max_connections);
823
824   /* find duplicate session */
825   t = plugin->head;
826   while (t != NULL)
827   {
828     if ((t->inbound) &&
829         (0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity)))
830         &&
831         /* FIXME add source address comparison */
832         (t->tag == tag))
833       break;
834     t = t->next;
835   }
836   if (t != NULL)
837   {
838     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
839                      "Duplicate session, dismissing new connection from peer `%s'\n",
840                      GNUNET_i2s (&target));
841     goto error;
842   }
843
844   /* find semi-session */
845   t = plugin->server_semi_head;
846
847   while (t != NULL)
848   {
849     /* FIXME add source address comparison */
850     if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity)))
851         && (t->tag == tag))
852     {
853       break;
854     }
855     t = t->next;
856   }
857
858   if (t == NULL)
859     goto create;
860   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
861                    "Found existing semi-session for `%s'\n",
862                    GNUNET_i2s (&target));
863
864   if ((direction == _SEND) && (t->server_send != NULL))
865   {
866     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
867                      "Duplicate GET session, dismissing new connection from peer `%s'\n",
868                      GNUNET_i2s (&target));
869     goto error;
870   }
871   else
872   {
873     s = t;
874     GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head,
875                                  plugin->server_semi_tail, s);
876     GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
877     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
878                      "Found matching semi-session, merging session for peer `%s'\n",
879                      GNUNET_i2s (&target));
880     GNUNET_assert (NULL != s);
881     goto found;
882   }
883   if ((direction == _RECEIVE) && (t->server_recv != NULL))
884   {
885     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
886                      "Duplicate PUT session, dismissing new connection from peer `%s'\n",
887                      GNUNET_i2s (&target));
888     goto error;
889   }
890   else
891   {
892     s = t;
893     GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head,
894                                  plugin->server_semi_tail, s);
895     GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
896     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
897                      "Found matching semi-session, merging session for peer `%s'\n",
898                      GNUNET_i2s (&target));
899     GNUNET_assert (NULL != s);
900     goto found;
901   }
902
903 create:
904 /* create new session */
905   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
906                    "Creating new session for peer `%s' \n",
907                    GNUNET_i2s (&target));
908   switch (conn_info->client_addr->sa_family)
909   {
910   case (AF_INET):
911     addr = http_common_address_from_socket (plugin->protocol, conn_info->client_addr, sizeof (struct sockaddr_in));
912     addr_len = http_common_address_get_size (addr);
913     ats = plugin->env->get_address_type (plugin->env->cls, conn_info->client_addr, sizeof (struct sockaddr_in));
914     break;
915   case (AF_INET6):
916     addr = http_common_address_from_socket (plugin->protocol, conn_info->client_addr, sizeof (struct sockaddr_in6));
917     addr_len = http_common_address_get_size (addr);
918     ats = plugin->env->get_address_type (plugin->env->cls, conn_info->client_addr, sizeof (struct sockaddr_in6));
919     break;
920   default:
921     GNUNET_break (0);
922     goto error;
923   }
924
925   s = GNUNET_malloc (sizeof (struct HttpServerSession));
926   memcpy (&s->target, &target, sizeof (struct GNUNET_PeerIdentity));
927   s->plugin = plugin;
928   s->addr = addr;
929   s->addrlen = addr_len;
930   s->ats_address_network_type = ats.value;
931   s->inbound = GNUNET_YES;
932   s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS;
933   s->tag = tag;
934   s->server_recv = NULL;
935   s->server_send = NULL;
936
937   server_start_session_timeout(s);
938
939   GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head,
940                                plugin->server_semi_tail, s);
941   goto found;
942 error:
943   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
944                    "Invalid connection request\n");
945   return NULL;
946
947 found:
948   sc = GNUNET_malloc (sizeof (struct ServerConnection));
949   sc->mhd_conn = mhd_connection;
950   sc->direction = direction;
951   sc->session = s;
952   if (direction == _SEND)
953     s->server_send = sc;
954   if (direction == _RECEIVE)
955     s->server_recv = sc;
956
957 #if MHD_VERSION >= 0x00090E00
958   int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
959
960   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
961                    "Setting timeout for %p to %u sec.\n", sc, to);
962   MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to);
963
964   struct MHD_Daemon *d = NULL;
965 #if 0
966   if (s->addrlen == sizeof (struct IPv6HttpAddress))
967     d = plugin->server_v6;
968   if (s->addrlen == sizeof (struct IPv4HttpAddress))
969     d = plugin->server_v4;
970 #endif
971   server_reschedule (plugin, d, GNUNET_NO);
972 #endif
973   return sc;
974
975 }
976
977 static struct HttpServerSession *
978 server_lookup_session (struct HTTP_Server_Plugin *plugin,
979                        struct ServerConnection * sc)
980 {
981   struct HttpServerSession *s;
982
983   for (s = plugin->head; NULL != s; s = s->next)
984     if ((s->server_recv == sc) || (s->server_send == sc))
985       return s;
986   for (s = plugin->server_semi_head; NULL != s; s = s->next)
987     if ((s->server_recv == sc) || (s->server_send == sc))
988       return s;
989   return NULL;
990 }
991
992
993 static int
994 server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
995                   const char *url, const char *method, const char *version,
996                   const char *upload_data, size_t * upload_data_size,
997                   void **httpSessionCache)
998 {
999   struct HTTP_Server_Plugin *plugin = cls;
1000   int res = MHD_YES;
1001
1002   struct ServerConnection *sc = *httpSessionCache;
1003   struct HttpServerSession *s;
1004   struct MHD_Response *response;
1005
1006   GNUNET_assert (cls != NULL);
1007   if (sc == NULL)
1008   {
1009     /* new connection */
1010     sc = server_lookup_connection (plugin, mhd_connection, url, method);
1011     if (sc != NULL)
1012       (*httpSessionCache) = sc;
1013     else
1014     {
1015       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
1016       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
1017       MHD_destroy_response (response);
1018       return res;
1019     }
1020   }
1021   else
1022   {
1023     /* 'old' connection */
1024     if (NULL == server_lookup_session (plugin, sc))
1025     {
1026       /* Session was already disconnected */
1027       return MHD_NO;
1028     }
1029   }
1030
1031   /* existing connection */
1032   sc = (*httpSessionCache);
1033   s = sc->session;
1034
1035   GNUNET_assert (NULL != s);
1036 #if 0
1037   /* connection is to be disconnected */
1038   if (sc->disconnect == GNUNET_YES)
1039   {
1040     /* Sent HTTP/1.1: 200 OK as PUT Response\ */
1041     response =
1042         MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!",
1043                                        MHD_NO, MHD_NO);
1044     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1045     MHD_destroy_response (response);
1046     return MHD_YES;
1047   }
1048
1049   GNUNET_assert (s != NULL);
1050   /* Check if both directions are connected */
1051   if ((sc->session->server_recv == NULL) || (sc->session->server_send == NULL))
1052   {
1053     /* Delayed read from since not both semi-connections are connected */
1054     return MHD_YES;
1055   }
1056
1057   if (sc->direction == _SEND)
1058   {
1059     response =
1060         MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
1061                                            32 * 1024,
1062                                            &server_send_callback, s,
1063                                            NULL);
1064     MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1065     MHD_destroy_response (response);
1066     return MHD_YES;
1067   }
1068   if (sc->direction == _RECEIVE)
1069   {
1070     if (*upload_data_size == 0)
1071     {
1072       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1073                        "Server: Peer `%s' PUT on address `%s' connected\n",
1074                        GNUNET_i2s (&s->target),
1075                        http_plugin_address_to_string (NULL, s->addr,
1076                                                       s->addrlen));
1077       return MHD_YES;
1078     }
1079
1080     /* Receiving data */
1081     if ((*upload_data_size > 0))
1082     {
1083       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1084                        "Server: peer `%s' PUT on address `%s' received %u bytes\n",
1085                        GNUNET_i2s (&s->target),
1086                        http_plugin_address_to_string (NULL, s->addr,
1087                                                       s->addrlen),
1088                        *upload_data_size);
1089       struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1090
1091       if ((s->next_receive.abs_value <= now.abs_value))
1092       {
1093         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1094                          "Server: %p: PUT with %u bytes forwarded to MST\n", s,
1095                          *upload_data_size);
1096         if (s->msg_tk == NULL)
1097         {
1098           s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s);
1099         }
1100             GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data,
1101                                        *upload_data_size, GNUNET_NO, GNUNET_NO);
1102
1103 #if MHD_VERSION >= 0x00090E00
1104         int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
1105         struct ServerConnection *t = NULL;
1106
1107         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1108                          "Server: Received %u bytes\n", *upload_data_size);
1109         /* Setting timeouts for other connections */
1110         if (s->server_recv != NULL)
1111         {
1112           t = s->server_recv;
1113           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1114                            "Server: Setting timeout for %p to %u sec.\n", t,
1115                            to);
1116           MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
1117                                      to);
1118         }
1119         if (s->server_send != NULL)
1120         {
1121           t = s->server_send;
1122           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1123                            "Server: Setting timeout for %p to %u sec.\n", t,
1124                            to);
1125           MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
1126                                      to);
1127         }
1128         struct MHD_Daemon *d = NULL;
1129
1130         if (s->addrlen == sizeof (struct IPv6HttpAddress))
1131           d = plugin->server_v6;
1132         if (s->addrlen == sizeof (struct IPv4HttpAddress))
1133           d = plugin->server_v4;
1134         server_reschedule (plugin, d, GNUNET_NO);
1135 #endif
1136         (*upload_data_size) = 0;
1137       }
1138       else
1139       {
1140         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1141                     "Server: %p no inbound bandwidth available! Next read was delayed by %llu ms\n",
1142                     s, now.abs_value - s->next_receive.abs_value);
1143       }
1144       return MHD_YES;
1145     }
1146     else
1147       return MHD_NO;
1148   }
1149 #endif
1150   return res;
1151 }
1152
1153 static void
1154 server_disconnect_cb (void *cls, struct MHD_Connection *connection,
1155                       void **httpSessionCache)
1156 {
1157   /* FIXME SPLIT */
1158   GNUNET_break (0);
1159 }
1160
1161 /**
1162  * Check if incoming connection is accepted.
1163  * NOTE: Here every connection is accepted
1164  * @param cls plugin as closure
1165  * @param addr address of incoming connection
1166  * @param addr_len address length of incoming connection
1167  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
1168  *
1169  */
1170 static int
1171 server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
1172 {
1173   struct HTTP_Server_Plugin *plugin = cls;
1174
1175   if (plugin->cur_connections <= plugin->max_connections)
1176     return MHD_YES;
1177   else
1178   {
1179     GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1180                      _("Server reached maximum number connections (%u), rejecting new connection\n"),
1181                      plugin->max_connections);
1182     return MHD_NO;
1183   }
1184 }
1185
1186 static void
1187 server_log (void *arg, const char *fmt, va_list ap)
1188 {
1189   char text[1024];
1190
1191   vsnprintf (text, sizeof (text), fmt, ap);
1192   va_end (ap);
1193   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Server: %s\n", text);
1194 }
1195
1196
1197 /**
1198  * Call MHD IPv4 to process pending requests and then go back
1199  * and schedule the next run.
1200  * @param cls plugin as closure
1201  * @param tc task context
1202  */
1203 static void
1204 server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1205 {
1206   struct HTTP_Server_Plugin *plugin = cls;
1207
1208   GNUNET_assert (cls != NULL);
1209
1210   plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1211   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1212     return;
1213 #if 0
1214   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1215                    "Running IPv4 server\n");
1216 #endif
1217   plugin->server_v4_immediately = GNUNET_NO;
1218   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4));
1219   server_reschedule (plugin, plugin->server_v4, GNUNET_NO);
1220 }
1221
1222
1223 /**
1224  * Call MHD IPv6 to process pending requests and then go back
1225  * and schedule the next run.
1226  * @param cls plugin as closure
1227  * @param tc task context
1228  */
1229 static void
1230 server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1231 {
1232   struct HTTP_Server_Plugin *plugin = cls;
1233
1234   GNUNET_assert (cls != NULL);
1235   plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1236   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1237     return;
1238 #if 0
1239   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1240                    "Running IPv6 server\n");
1241 #endif
1242   plugin->server_v6_immediately = GNUNET_NO;
1243   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6));
1244   server_reschedule (plugin, plugin->server_v6, GNUNET_NO);
1245 }
1246
1247
1248 /**
1249  * Function that queries MHD's select sets and
1250  * starts the task waiting for them.
1251  * @param plugin plugin
1252  * @param daemon_handle the MHD daemon handle
1253  * @return gnunet task identifier
1254  */
1255 static GNUNET_SCHEDULER_TaskIdentifier
1256 server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_handle,
1257                  int now)
1258 {
1259   GNUNET_SCHEDULER_TaskIdentifier ret;
1260   fd_set rs;
1261   fd_set ws;
1262   fd_set es;
1263   struct GNUNET_NETWORK_FDSet *wrs;
1264   struct GNUNET_NETWORK_FDSet *wws;
1265   struct GNUNET_NETWORK_FDSet *wes;
1266   int max;
1267   unsigned MHD_LONG_LONG timeout;
1268   static unsigned long long last_timeout = 0;
1269   int haveto;
1270
1271   struct GNUNET_TIME_Relative tv;
1272
1273   ret = GNUNET_SCHEDULER_NO_TASK;
1274   FD_ZERO (&rs);
1275   FD_ZERO (&ws);
1276   FD_ZERO (&es);
1277   wrs = GNUNET_NETWORK_fdset_create ();
1278   wes = GNUNET_NETWORK_fdset_create ();
1279   wws = GNUNET_NETWORK_fdset_create ();
1280   max = -1;
1281   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
1282   haveto = MHD_get_timeout (daemon_handle, &timeout);
1283   if (haveto == MHD_YES)
1284   {
1285     if (timeout != last_timeout)
1286     {
1287 #if VERBOSE_SERVER
1288       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1289                        "SELECT Timeout changed from %llu to %llu\n",
1290                        last_timeout, timeout);
1291 #endif
1292       last_timeout = timeout;
1293     }
1294     tv.rel_value = (uint64_t) timeout;
1295   }
1296   else
1297     tv = GNUNET_TIME_UNIT_SECONDS;
1298   /* Force immediate run, since we have outbound data to send */
1299   if (now == GNUNET_YES)
1300     tv = GNUNET_TIME_UNIT_MILLISECONDS;
1301   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1302   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1303   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1304
1305   if (daemon_handle == plugin->server_v4)
1306   {
1307     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1308     {
1309       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1310       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1311     }
1312 #if 0
1313     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1314                      "Scheduling IPv4 server task in %llu ms\n", tv);
1315 #endif
1316     ret =
1317         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1318                                      tv, wrs, wws,
1319                                      &server_v4_run, plugin);
1320   }
1321   if (daemon_handle == plugin->server_v6)
1322   {
1323     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1324     {
1325       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1326       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1327     }
1328 #if 0
1329     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1330                      "Scheduling IPv6 server task in %llu ms\n", tv);
1331 #endif
1332     ret =
1333         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1334                                      tv, wrs, wws,
1335                                      &server_v6_run, plugin);
1336   }
1337   GNUNET_NETWORK_fdset_destroy (wrs);
1338   GNUNET_NETWORK_fdset_destroy (wws);
1339   GNUNET_NETWORK_fdset_destroy (wes);
1340   return ret;
1341 }
1342
1343
1344 #if BUILD_HTTPS
1345 static char *
1346 server_load_file (const char *file)
1347 {
1348   struct GNUNET_DISK_FileHandle *gn_file;
1349   uint64_t fsize;
1350   char *text = NULL;
1351
1352   if (GNUNET_OK != GNUNET_DISK_file_size (file,
1353       &fsize, GNUNET_NO, GNUNET_YES))
1354     return NULL;
1355   text = GNUNET_malloc (fsize + 1);
1356   gn_file =
1357       GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
1358                              GNUNET_DISK_PERM_USER_READ);
1359   if (gn_file == NULL)
1360   {
1361     GNUNET_free (text);
1362     return NULL;
1363   }
1364   if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fsize))
1365   {
1366     GNUNET_free (text);
1367     GNUNET_DISK_file_close (gn_file);
1368     return NULL;
1369   }
1370   text[fsize] = '\0';
1371   GNUNET_DISK_file_close (gn_file);
1372   return text;
1373 }
1374 #endif
1375
1376
1377 #if BUILD_HTTPS
1378
1379 static int
1380 server_load_certificate (struct HTTP_Server_Plugin *plugin)
1381 {
1382   int res = GNUNET_OK;
1383
1384   char *key_file;
1385   char *cert_file;
1386
1387   /* Get crypto init string from config
1388    * If not present just use default values */
1389
1390   if (GNUNET_OK ==
1391                  GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1392                                                         plugin->name,
1393                                                         "CRYPTO_INIT",
1394                                                         &plugin->crypto_init))
1395       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1396                        "Using crypto init string `%s'\n",
1397                        plugin->crypto_init);
1398   else
1399     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1400                      "Using default crypto init string \n");
1401
1402   if (GNUNET_OK !=
1403       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
1404                                                "KEY_FILE", &key_file))
1405   {
1406     key_file = GNUNET_strdup ("https_key.key");
1407   }
1408
1409   if (GNUNET_OK !=
1410       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
1411                                                "CERT_FILE", &cert_file))
1412   {
1413     GNUNET_asprintf (&cert_file, "%s", "https_cert.crt");
1414   }
1415
1416   /* read key & certificates from file */
1417   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1418               "Trying to loading TLS certificate from key-file `%s' cert-file`%s'\n",
1419               key_file, cert_file);
1420
1421   plugin->key = server_load_file (key_file);
1422   plugin->cert = server_load_file (cert_file);
1423
1424   if ((plugin->key == NULL) || (plugin->cert == NULL))
1425   {
1426     struct GNUNET_OS_Process *cert_creation;
1427
1428     GNUNET_free_non_null (plugin->key);
1429     plugin->key = NULL;
1430     GNUNET_free_non_null (plugin->cert);
1431     plugin->cert = NULL;
1432
1433     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1434                 "No usable TLS certificate found, creating certificate\n");
1435     errno = 0;
1436     cert_creation =
1437         GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL,
1438                                  "gnunet-transport-certificate-creation",
1439                                  "gnunet-transport-certificate-creation",
1440                                  key_file, cert_file, NULL);
1441     if (cert_creation == NULL)
1442     {
1443       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1444                        _
1445                        ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
1446       GNUNET_free (key_file);
1447       GNUNET_free (cert_file);
1448
1449       GNUNET_free_non_null (plugin->key);
1450       plugin->key = NULL;
1451       GNUNET_free_non_null (plugin->cert);
1452       plugin->cert = NULL;
1453       GNUNET_free_non_null (plugin->crypto_init);
1454       plugin->crypto_init = NULL;
1455
1456       return GNUNET_SYSERR;
1457     }
1458     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation));
1459     GNUNET_OS_process_destroy (cert_creation);
1460
1461     plugin->key = server_load_file (key_file);
1462     plugin->cert = server_load_file (cert_file);
1463   }
1464
1465   if ((plugin->key == NULL) || (plugin->cert == NULL))
1466   {
1467     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1468                      _
1469                      ("No usable TLS certificate found and creating one failed!\n"),
1470                      "transport-https");
1471     GNUNET_free (key_file);
1472     GNUNET_free (cert_file);
1473
1474     GNUNET_free_non_null (plugin->key);
1475     plugin->key = NULL;
1476     GNUNET_free_non_null (plugin->cert);
1477     plugin->cert = NULL;
1478     GNUNET_free_non_null (plugin->crypto_init);
1479     plugin->crypto_init = NULL;
1480
1481     return GNUNET_SYSERR;
1482   }
1483   GNUNET_free (key_file);
1484   GNUNET_free (cert_file);
1485   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
1486   return res;
1487 }
1488 #endif
1489
1490 int
1491 server_start (struct HTTP_Server_Plugin *plugin)
1492 {
1493   unsigned int timeout;
1494   GNUNET_assert (NULL != plugin);
1495
1496 #if BUILD_HTTPS
1497   if (GNUNET_SYSERR == server_load_certificate (plugin))
1498   {
1499     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1500                      "Could not load or create server certificate! Loading plugin failed!\n");
1501     return GNUNET_SYSERR;
1502   }
1503 #endif
1504
1505
1506 #if MHD_VERSION >= 0x00090E00
1507   timeout = HTTP_NOT_VALIDATED_TIMEOUT.rel_value / 1000;
1508   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1509                    "MHD can set timeout per connection! Default time out %u sec.\n",
1510                    timeout);
1511 #else
1512   timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000;
1513   GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1514                    "MHD cannot set timeout per connection! Default time out %u sec.\n",
1515                    timeout);
1516 #endif
1517   plugin->server_v4 = NULL;
1518   if (plugin->use_ipv4 == GNUNET_YES)
1519   {
1520     plugin->server_v4 = MHD_start_daemon (
1521 #if VERBOSE_SERVER
1522                                            MHD_USE_DEBUG |
1523 #endif
1524 #if BUILD_HTTPS
1525                                            MHD_USE_SSL |
1526 #endif
1527                                            MHD_NO_FLAG, plugin->port,
1528                                            &server_accept_cb, plugin,
1529                                            &server_access_cb, plugin,
1530                                            MHD_OPTION_SOCK_ADDR,
1531                                            (struct sockaddr_in *)
1532                                            plugin->server_addr_v4,
1533                                            MHD_OPTION_CONNECTION_LIMIT,
1534                                            (unsigned int)
1535                                            plugin->max_connections,
1536 #if BUILD_HTTPS
1537                                            MHD_OPTION_HTTPS_PRIORITIES,
1538                                            plugin->crypto_init,
1539                                            MHD_OPTION_HTTPS_MEM_KEY,
1540                                            plugin->key,
1541                                            MHD_OPTION_HTTPS_MEM_CERT,
1542                                            plugin->cert,
1543 #endif
1544                                            MHD_OPTION_CONNECTION_TIMEOUT,
1545                                            timeout,
1546                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1547                                            (size_t) (2 *
1548                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1549                                            MHD_OPTION_NOTIFY_COMPLETED,
1550                                            &server_disconnect_cb, plugin,
1551                                            MHD_OPTION_EXTERNAL_LOGGER,
1552                                            server_log, NULL, MHD_OPTION_END);
1553   }
1554   plugin->server_v6 = NULL;
1555   if (plugin->use_ipv6 == GNUNET_YES)
1556   {
1557     plugin->server_v6 = MHD_start_daemon (
1558 #if VERBOSE_SERVER
1559                                            MHD_USE_DEBUG |
1560 #endif
1561 #if BUILD_HTTPS
1562                                            MHD_USE_SSL |
1563 #endif
1564                                            MHD_USE_IPv6, plugin->port,
1565                                            &server_accept_cb, plugin,
1566                                            &server_access_cb, plugin,
1567                                            MHD_OPTION_SOCK_ADDR,
1568                                            (struct sockaddr_in6 *)
1569                                            plugin->server_addr_v6,
1570                                            MHD_OPTION_CONNECTION_LIMIT,
1571                                            (unsigned int)
1572                                            plugin->max_connections,
1573 #if BUILD_HTTPS
1574                                            MHD_OPTION_HTTPS_PRIORITIES,
1575                                            plugin->crypto_init,
1576                                            MHD_OPTION_HTTPS_MEM_KEY,
1577                                            plugin->key,
1578                                            MHD_OPTION_HTTPS_MEM_CERT,
1579                                            plugin->cert,
1580 #endif
1581                                            MHD_OPTION_CONNECTION_TIMEOUT,
1582                                            timeout,
1583                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1584                                            (size_t) (2 *
1585                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1586                                            MHD_OPTION_NOTIFY_COMPLETED,
1587                                            &server_disconnect_cb, plugin,
1588                                            MHD_OPTION_EXTERNAL_LOGGER,
1589                                            server_log, NULL, MHD_OPTION_END);
1590
1591   }
1592
1593   if ((plugin->use_ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL))
1594   {
1595     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1596                      "Failed to start %s IPv4 server component on port %u\n",
1597                      plugin->name, plugin->port);
1598     return GNUNET_SYSERR;
1599   }
1600   server_reschedule (plugin, plugin->server_v4, GNUNET_NO);
1601
1602   if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL))
1603   {
1604     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1605                      "Failed to start %s IPv6 server component on port %u\n",
1606                      plugin->name, plugin->port);
1607     return GNUNET_SYSERR;
1608   }
1609   server_reschedule (plugin, plugin->server_v6, GNUNET_NO);
1610   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1611                    "%s server component started on port %u\n", plugin->name,
1612                    plugin->port);
1613   return GNUNET_OK;
1614 }
1615
1616
1617 void
1618 server_stop (struct HTTP_Server_Plugin *plugin)
1619 {
1620   struct HttpServerSession *s = NULL;
1621   struct HttpServerSession *t = NULL;
1622
1623   struct MHD_Daemon *server_v4_tmp = plugin->server_v4;
1624   plugin->server_v4 = NULL;
1625
1626   struct MHD_Daemon *server_v6_tmp = plugin->server_v6;
1627   plugin->server_v6 = NULL;
1628
1629   if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1630   {
1631     GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1632     plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1633   }
1634
1635   if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1636   {
1637     GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1638     plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1639   }
1640
1641   if (server_v6_tmp != NULL)
1642   {
1643     MHD_stop_daemon (server_v4_tmp);
1644   }
1645   if (server_v6_tmp != NULL)
1646   {
1647     MHD_stop_daemon (server_v6_tmp);
1648   }
1649
1650   /* cleaning up semi-sessions never propagated */
1651   s = plugin->server_semi_head;
1652   while (s != NULL)
1653   {
1654 #if VERBOSE_SERVER
1655     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1656                      "Deleting semi-sessions %p\n", s);
1657 #endif
1658     t = s->next;
1659     struct HTTP_Message *msg = s->msg_head;
1660     struct HTTP_Message *tmp = NULL;
1661
1662     while (msg != NULL)
1663     {
1664       tmp = msg->next;
1665
1666       GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
1667       if (msg->transmit_cont != NULL)
1668       {
1669         msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
1670       }
1671       GNUNET_free (msg);
1672       msg = tmp;
1673     }
1674
1675     server_delete_session (s);
1676     s = t;
1677   }
1678
1679   p = NULL;
1680
1681 #if BUILD_HTTPS
1682   GNUNET_free_non_null (plugin->crypto_init);
1683   GNUNET_free_non_null (plugin->cert);
1684   GNUNET_free_non_null (plugin->key);
1685 #endif
1686
1687   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1688                    "%s server component stopped\n", plugin->name);
1689 }
1690
1691 static void
1692 server_add_address (void *cls, int add_remove, const struct sockaddr *addr,
1693                  socklen_t addrlen)
1694 {
1695   struct HTTP_Server_Plugin *plugin = cls;
1696   struct HttpAddressWrapper *w = NULL;
1697
1698   if ((AF_INET == addr->sa_family) && (GNUNET_NO == plugin->use_ipv4))
1699     return;
1700
1701   if ((AF_INET6 == addr->sa_family) && (GNUNET_NO == plugin->use_ipv6))
1702     return;
1703
1704   w = GNUNET_malloc (sizeof (struct HttpAddressWrapper));
1705   w->addr = http_common_address_from_socket (plugin->protocol, addr, addrlen);
1706   if (NULL == w->addr)
1707   {
1708     GNUNET_free (w);
1709     return;
1710   }
1711   w->addrlen = http_common_address_get_size (w->addr);
1712
1713   GNUNET_CONTAINER_DLL_insert(plugin->addr_head, plugin->addr_tail, w);
1714   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1715                    "Notifying transport to add address `%s'\n",
1716                    http_common_plugin_address_to_string(NULL, w->addr, w->addrlen));
1717
1718   plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen);
1719 }
1720
1721
1722 static void
1723 server_remove_address (void *cls, int add_remove, const struct sockaddr *addr,
1724                     socklen_t addrlen)
1725 {
1726   struct HTTP_Server_Plugin *plugin = cls;
1727   struct HttpAddressWrapper *w = plugin->addr_head;
1728   size_t saddr_len;
1729   void * saddr = http_common_address_from_socket (plugin->protocol, addr, addrlen);
1730   if (NULL == saddr)
1731     return;
1732   saddr_len =  http_common_address_get_size (saddr);
1733
1734   while (NULL != w)
1735   {
1736       if (GNUNET_YES == http_common_cmp_addresses(w->addr, w->addrlen, saddr, saddr_len))
1737         break;
1738       w = w->next;
1739   }
1740   GNUNET_free (saddr);
1741
1742   if (NULL == w)
1743     return;
1744
1745   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1746                    "Notifying transport to remove address `%s'\n",
1747                    http_common_plugin_address_to_string (NULL, w->addr, w->addrlen));
1748   GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w);
1749   plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen);
1750   GNUNET_free (w->addr);
1751   GNUNET_free (w);
1752 }
1753
1754
1755
1756 /**
1757  * Our external IP address/port mapping has changed.
1758  *
1759  * @param cls closure, the 'struct LocalAddrList'
1760  * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
1761  *     the previous (now invalid) one
1762  * @param addr either the previous or the new public IP address
1763  * @param addrlen actual lenght of the address
1764  */
1765 static void
1766 server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr *addr,
1767                        socklen_t addrlen)
1768 {
1769   GNUNET_assert (cls != NULL);
1770   struct HTTP_Server_Plugin *plugin = cls;
1771
1772   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1773                    "NPMC called %s to address `%s'\n",
1774                    (add_remove == GNUNET_NO) ? "remove" : "add",
1775                    GNUNET_a2s (addr, addrlen));
1776
1777   switch (add_remove)
1778   {
1779   case GNUNET_YES:
1780     server_add_address (cls, add_remove, addr, addrlen);
1781     break;
1782   case GNUNET_NO:
1783     server_remove_address (cls, add_remove, addr, addrlen);
1784     break;
1785   }
1786 }
1787
1788
1789 static int
1790 server_get_addresses (struct HTTP_Server_Plugin *plugin,
1791                       const char *serviceName,
1792                       const struct GNUNET_CONFIGURATION_Handle *cfg,
1793                       struct sockaddr ***addrs, socklen_t ** addr_lens)
1794 {
1795   int disablev6;
1796   unsigned long long port;
1797   struct addrinfo hints;
1798   struct addrinfo *res;
1799   struct addrinfo *pos;
1800   struct addrinfo *next;
1801   unsigned int i;
1802   int resi;
1803   int ret;
1804   struct sockaddr **saddrs;
1805   socklen_t *saddrlens;
1806   char *hostname;
1807
1808   *addrs = NULL;
1809   *addr_lens = NULL;
1810
1811   disablev6 = !plugin->use_ipv6;
1812
1813   port = 0;
1814   if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "PORT"))
1815   {
1816     GNUNET_break (GNUNET_OK ==
1817                   GNUNET_CONFIGURATION_get_value_number (cfg, serviceName,
1818                                                          "PORT", &port));
1819     if (port > 65535)
1820     {
1821       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1822                   _
1823                   ("Require valid port number for service in configuration!\n"));
1824       return GNUNET_SYSERR;
1825     }
1826   }
1827   if (0 == port)
1828   {
1829     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name,
1830                      "Starting in listen only mode\n");
1831     return -1; /* listen only */
1832   }
1833
1834
1835   if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "BINDTO"))
1836   {
1837     GNUNET_break (GNUNET_OK ==
1838                   GNUNET_CONFIGURATION_get_value_string (cfg, serviceName,
1839                                                          "BINDTO", &hostname));
1840   }
1841   else
1842     hostname = NULL;
1843
1844   if (hostname != NULL)
1845   {
1846     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1847                      "Resolving `%s' since that is where `%s' will bind to.\n",
1848                      hostname, serviceName);
1849     memset (&hints, 0, sizeof (struct addrinfo));
1850     if (disablev6)
1851       hints.ai_family = AF_INET;
1852     if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) ||
1853         (res == NULL))
1854     {
1855       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to resolve `%s': %s\n"),
1856                   hostname, gai_strerror (ret));
1857       GNUNET_free (hostname);
1858       return GNUNET_SYSERR;
1859     }
1860     next = res;
1861     i = 0;
1862     while (NULL != (pos = next))
1863     {
1864       next = pos->ai_next;
1865       if ((disablev6) && (pos->ai_family == AF_INET6))
1866         continue;
1867       i++;
1868     }
1869     if (0 == i)
1870     {
1871       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1872                   _("Failed to find %saddress for `%s'.\n"),
1873                   disablev6 ? "IPv4 " : "", hostname);
1874       freeaddrinfo (res);
1875       GNUNET_free (hostname);
1876       return GNUNET_SYSERR;
1877     }
1878     resi = i;
1879     saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
1880     saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
1881     i = 0;
1882     next = res;
1883     while (NULL != (pos = next))
1884     {
1885       next = pos->ai_next;
1886       if ((disablev6) && (pos->ai_family == AF_INET6))
1887         continue;
1888       if ((pos->ai_protocol != IPPROTO_TCP) && (pos->ai_protocol != 0))
1889         continue;               /* not TCP */
1890       if ((pos->ai_socktype != SOCK_STREAM) && (pos->ai_socktype != 0))
1891         continue;               /* huh? */
1892       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1893                        "Service will bind to `%s'\n", GNUNET_a2s (pos->ai_addr,
1894                                                                   pos->ai_addrlen));
1895       if (pos->ai_family == AF_INET)
1896       {
1897         GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in));
1898         saddrlens[i] = pos->ai_addrlen;
1899         saddrs[i] = GNUNET_malloc (saddrlens[i]);
1900         memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
1901         ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1902       }
1903       else
1904       {
1905         GNUNET_assert (pos->ai_family == AF_INET6);
1906         GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6));
1907         saddrlens[i] = pos->ai_addrlen;
1908         saddrs[i] = GNUNET_malloc (saddrlens[i]);
1909         memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
1910         ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
1911       }
1912       i++;
1913     }
1914     GNUNET_free (hostname);
1915     freeaddrinfo (res);
1916     resi = i;
1917   }
1918   else
1919   {
1920     /* will bind against everything, just set port */
1921     if (disablev6)
1922     {
1923       /* V4-only */
1924       resi = 1;
1925       i = 0;
1926       saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
1927       saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
1928
1929       saddrlens[i] = sizeof (struct sockaddr_in);
1930       saddrs[i] = GNUNET_malloc (saddrlens[i]);
1931 #if HAVE_SOCKADDR_IN_SIN_LEN
1932       ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i];
1933 #endif
1934       ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
1935       ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1936     }
1937     else
1938     {
1939       /* dual stack */
1940       resi = 2;
1941       saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
1942       saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
1943       i = 0;
1944       saddrlens[i] = sizeof (struct sockaddr_in6);
1945       saddrs[i] = GNUNET_malloc (saddrlens[i]);
1946 #if HAVE_SOCKADDR_IN_SIN_LEN
1947       ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0];
1948 #endif
1949       ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6;
1950       ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
1951       i++;
1952       saddrlens[i] = sizeof (struct sockaddr_in);
1953       saddrs[i] = GNUNET_malloc (saddrlens[i]);
1954 #if HAVE_SOCKADDR_IN_SIN_LEN
1955       ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1];
1956 #endif
1957       ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
1958       ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1959     }
1960   }
1961   *addrs = saddrs;
1962   *addr_lens = saddrlens;
1963   return resi;
1964 }
1965
1966 static void
1967 server_start_report_addresses (struct HTTP_Server_Plugin *plugin)
1968 {
1969   int res = GNUNET_OK;
1970   struct sockaddr **addrs;
1971   socklen_t *addrlens;
1972
1973   res = server_get_addresses (plugin,
1974                               plugin->name, plugin->env->cfg,
1975                               &addrs, &addrlens);
1976   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1977                    _("Found %u addresses to report to NAT service\n"), res);
1978
1979   if (GNUNET_SYSERR == res)
1980   {
1981     plugin->nat = NULL;
1982     return;
1983   }
1984
1985   plugin->nat =
1986       GNUNET_NAT_register (plugin->env->cfg, GNUNET_YES, plugin->port,
1987                            (unsigned int) res,
1988                            (const struct sockaddr **) addrs, addrlens,
1989                            &server_nat_port_map_callback, NULL, plugin);
1990   while (res > 0)
1991   {
1992     res--;
1993     GNUNET_assert (addrs[res] != NULL);
1994     GNUNET_free (addrs[res]);
1995   }
1996   GNUNET_free_non_null (addrs);
1997   GNUNET_free_non_null (addrlens);
1998 }
1999
2000
2001 static void
2002 server_stop_report_addresses (struct HTTP_Server_Plugin *plugin)
2003 {
2004   /* Stop NAT handle */
2005   if (NULL != plugin->nat)
2006     GNUNET_NAT_unregister (plugin->nat);
2007
2008   /* Clean up addresses */
2009   struct HttpAddressWrapper *w;
2010
2011   while (plugin->addr_head != NULL)
2012   {
2013     w = plugin->addr_head;
2014     GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w);
2015     GNUNET_free (w->addr);
2016     GNUNET_free (w);
2017   }
2018 }
2019
2020
2021 /**
2022  * Check if IPv6 supported on this system
2023  */
2024 static int
2025 server_check_ipv6_support (struct HTTP_Server_Plugin *plugin)
2026 {
2027   struct GNUNET_NETWORK_Handle *desc = NULL;
2028   int res = GNUNET_NO;
2029
2030   /* Probe IPv6 support */
2031   desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
2032   if (NULL == desc)
2033   {
2034     if ((errno == ENOBUFS) || (errno == ENOMEM) || (errno == ENFILE) ||
2035         (errno == EACCES))
2036     {
2037       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
2038     }
2039     GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
2040                      _
2041                      ("Disabling IPv6 since it is not supported on this system!\n"));
2042     res = GNUNET_NO;
2043   }
2044   else
2045   {
2046     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
2047     desc = NULL;
2048     res = GNUNET_YES;
2049   }
2050   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2051                    "Testing IPv6 on this system: %s\n",
2052                    (res == GNUNET_YES) ? "successful" : "failed");
2053   return res;
2054 }
2055
2056
2057 /**
2058  * Function called when the service shuts down.  Unloads our plugins
2059  * and cancels pending validations.
2060  *
2061  * @param cls closure, unused
2062  * @param tc task context (unused)
2063  */
2064 static void
2065 server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2066 {
2067   struct HTTP_Server_Plugin *plugin = cls;
2068
2069   plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK;
2070
2071   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2072     return;
2073
2074   GNUNET_asprintf(&plugin->ext_addr, "%s://%s", plugin->protocol, plugin->external_hostname);
2075   plugin->ext_addr_len = strlen (plugin->ext_addr) + 1;
2076   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2077                    "Notifying transport about external hostname address `%s'\n", plugin->ext_addr);
2078   plugin->env->notify_address (plugin->env->cls, GNUNET_YES, plugin->ext_addr, plugin->ext_addr_len );
2079 }
2080
2081
2082 static int
2083 server_configure_plugin (struct HTTP_Server_Plugin *plugin)
2084 {
2085   unsigned long long port;
2086   unsigned long long max_connections;
2087   char *bind4_address = NULL;
2088   char *bind6_address = NULL;
2089
2090   /* Use IPv4? */
2091   if (GNUNET_CONFIGURATION_have_value
2092       (plugin->env->cfg, plugin->name, "USE_IPv4"))
2093   {
2094     plugin->use_ipv4 =
2095         GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name,
2096                                               "USE_IPv4");
2097   }
2098   else
2099     plugin->use_ipv4 = GNUNET_YES;
2100   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2101                    _("IPv4 support is %s\n"),
2102                    (plugin->use_ipv4 == GNUNET_YES) ? "enabled" : "disabled");
2103
2104   /* Use IPv6? */
2105   if (GNUNET_CONFIGURATION_have_value
2106       (plugin->env->cfg, plugin->name, "USE_IPv6"))
2107   {
2108     plugin->use_ipv6 =
2109         GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name,
2110                                               "USE_IPv6");
2111   }
2112   else
2113     plugin->use_ipv6 = GNUNET_YES;
2114   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2115                    _("IPv6 support is %s\n"),
2116                    (plugin->use_ipv6 == GNUNET_YES) ? "enabled" : "disabled");
2117
2118   if ((plugin->use_ipv4 == GNUNET_NO) && (plugin->use_ipv6 == GNUNET_NO))
2119   {
2120     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2121                      _
2122                      ("Neither IPv4 nor IPv6 are enabled! Fix in configuration\n"),
2123                      plugin->name);
2124     return GNUNET_SYSERR;
2125   }
2126
2127   /* Reading port number from config file */
2128   if ((GNUNET_OK !=
2129        GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, plugin->name,
2130                                               "PORT", &port)) || (port > 65535))
2131   {
2132     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2133                      _("Port is required! Fix in configuration\n"),
2134                      plugin->name);
2135     return GNUNET_SYSERR;
2136   }
2137   plugin->port = port;
2138
2139   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2140                    _("Using port %u\n"), plugin->port);
2141
2142   if ((plugin->use_ipv4 == GNUNET_YES) &&
2143       (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2144                           plugin->name, "BINDTO", &bind4_address)))
2145   {
2146     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2147                      "Binding %s plugin to specific IPv4 address: `%s'\n",
2148                      plugin->protocol, bind4_address);
2149     plugin->server_addr_v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
2150     if (1 != inet_pton (AF_INET, bind4_address,
2151                         &plugin->server_addr_v4->sin_addr))
2152     {
2153         GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2154                          _
2155                          ("Specific IPv4 address `%s' in configuration file is invalid!\n"),
2156                          bind4_address);
2157       GNUNET_free (bind4_address);
2158       GNUNET_free (plugin->server_addr_v4);
2159       plugin->server_addr_v4 = NULL;
2160       return GNUNET_SYSERR;
2161     }
2162     else
2163     {
2164       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2165                          _("Binding to IPv4 address %s\n"), bind4_address);
2166       plugin->server_addr_v4->sin_family = AF_INET;
2167       plugin->server_addr_v4->sin_port = htons (plugin->port);
2168     }
2169     GNUNET_free (bind4_address);
2170   }
2171
2172   if ((plugin->use_ipv6 == GNUNET_YES) &&
2173       (GNUNET_YES ==
2174        GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
2175                                               "BINDTO6", &bind6_address)))
2176   {
2177     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2178                      "Binding %s plugin to specific IPv6 address: `%s'\n",
2179                      plugin->protocol, bind6_address);
2180     plugin->server_addr_v6 = GNUNET_malloc (sizeof (struct sockaddr_in6));
2181     if (1 !=
2182         inet_pton (AF_INET6, bind6_address, &plugin->server_addr_v6->sin6_addr))
2183     {
2184       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2185                        _
2186                        ("Specific IPv6 address `%s' in configuration file is invalid!\n"),
2187                        bind6_address);
2188       GNUNET_free (bind6_address);
2189       GNUNET_free (plugin->server_addr_v6);
2190       plugin->server_addr_v6 = NULL;
2191       return GNUNET_SYSERR;
2192     }
2193     else
2194     {
2195       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2196                          _("Binding to IPv6 address %s\n"), bind6_address);
2197       plugin->server_addr_v6->sin6_family = AF_INET6;
2198       plugin->server_addr_v6->sin6_port = htons (plugin->port);
2199     }
2200     GNUNET_free (bind6_address);
2201   }
2202
2203   if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
2204                                               "EXTERNAL_HOSTNAME", &plugin->external_hostname))
2205   {
2206       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2207                        _("Using external hostname `%s'\n"), plugin->external_hostname);
2208       plugin->notify_ext_task = GNUNET_SCHEDULER_add_now (&server_notify_external_hostname, plugin);
2209   }
2210   else
2211     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2212                      "No external hostname configured\n");
2213
2214
2215   /* Optional parameters */
2216   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
2217                       plugin->name,
2218                       "MAX_CONNECTIONS", &max_connections))
2219     max_connections = 128;
2220   plugin->max_connections = max_connections;
2221
2222   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2223                    _("Maximum number of connections is %u\n"),
2224                    plugin->max_connections);
2225   return GNUNET_OK;
2226 }
2227
2228
2229 static void
2230 server_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2231 {
2232   GNUNET_assert (NULL != cls);
2233   struct HttpServerSession *s = cls;
2234
2235   s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
2236   GNUNET_log (TIMEOUT_LOG,
2237               "Session %p was idle for %llu ms, disconnecting\n",
2238               s, (unsigned long long) TIMEOUT.rel_value);
2239
2240   /* call session destroy function */
2241  GNUNET_assert (GNUNET_OK == server_disconnect (s));
2242 }
2243
2244 /**
2245 * Start session timeout
2246 */
2247 static void
2248 server_start_session_timeout (struct HttpServerSession *s)
2249 {
2250  GNUNET_assert (NULL != s);
2251  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s->timeout_task);
2252  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (TIMEOUT,
2253                                                   &server_session_timeout,
2254                                                   s);
2255  GNUNET_log (TIMEOUT_LOG,
2256              "Timeout for session %p set to %llu ms\n",
2257              s,  (unsigned long long) TIMEOUT.rel_value);
2258 }
2259
2260 #if 0
2261 /**
2262  * Session was idle, so disconnect it
2263  */
2264
2265
2266 /**
2267 * Increment session timeout due to activity
2268 */
2269 static void
2270 server_reschedule_session_timeout (struct HttpServerSession *s)
2271 {
2272  GNUNET_assert (NULL != s);
2273  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task);
2274
2275  GNUNET_SCHEDULER_cancel (s->timeout_task);
2276  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (TIMEOUT,
2277                                                   &session_timeout,
2278                                                   s);
2279  GNUNET_log (TIMEOUT_LOG,
2280              "Timeout rescheduled for session %p set to %llu ms\n",
2281              s, (unsigned long long) TIMEOUT.rel_value);
2282 }
2283 #endif
2284
2285 /**
2286  * Exit point from the plugin.
2287  */
2288 void *
2289 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
2290 {
2291   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2292   struct HTTP_Server_Plugin *plugin = api->cls;
2293
2294   if (GNUNET_SCHEDULER_NO_TASK != plugin->notify_ext_task)
2295   {
2296       GNUNET_SCHEDULER_cancel (plugin->notify_ext_task);
2297       plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK;
2298   }
2299
2300   if (NULL != plugin->ext_addr)
2301   {
2302       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2303                        "Notifying transport to remove address `%s'\n",
2304                        http_common_plugin_address_to_string (NULL,
2305                            plugin->ext_addr,
2306                            plugin->ext_addr_len));
2307       plugin->env->notify_address (plugin->env->cls,
2308                                    GNUNET_NO,
2309                                    plugin->ext_addr,
2310                                    plugin->ext_addr_len);
2311   }
2312
2313   /* Stop to report addresses to transport service */
2314   server_stop_report_addresses (plugin);
2315
2316   server_stop (plugin);
2317
2318   /* Clean up */
2319   GNUNET_free_non_null (plugin->external_hostname);
2320   GNUNET_free_non_null (plugin->ext_addr);
2321   GNUNET_free_non_null (plugin->server_addr_v4);
2322   GNUNET_free_non_null (plugin->server_addr_v6);
2323
2324   GNUNET_free (plugin);
2325   GNUNET_free (api);
2326   return NULL;
2327 }
2328
2329
2330 /**
2331  * Entry point for the plugin.
2332  */
2333 void *
2334 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
2335 {
2336   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2337   struct GNUNET_TRANSPORT_PluginFunctions *api;
2338   struct HTTP_Server_Plugin *plugin;
2339
2340   plugin = GNUNET_malloc (sizeof (struct HTTP_Server_Plugin));
2341   plugin->env = env;
2342   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2343   api->cls = plugin;
2344   api->send = &http_server_plugin_send;
2345   api->disconnect = &http_server_plugin_disconnect;
2346   api->check_address = &http_server_plugin_address_suggested;
2347   api->get_session = &http_server_plugin_get_session;
2348
2349   api->address_to_string = &http_common_plugin_address_to_string;
2350   api->string_to_address = &http_common_plugin_string_to_address;
2351   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2352
2353 #if BUILD_HTTPS
2354   plugin->name = "transport-https_server";
2355   plugin->protocol = "https";
2356 #else
2357   plugin->name = "transport-http_server";
2358   plugin->protocol = "http";
2359 #endif
2360
2361   /* Configure plugin */
2362   if (GNUNET_SYSERR == server_configure_plugin (plugin))
2363   {
2364       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2365       return NULL;
2366   }
2367
2368   /* Check IPv6 support */
2369   if (GNUNET_YES == plugin->use_ipv6)
2370     plugin->use_ipv6 = server_check_ipv6_support (plugin);
2371
2372   /* Report addresses to transport service */
2373   server_start_report_addresses (plugin);
2374
2375   if (GNUNET_SYSERR == server_start (plugin))
2376   {
2377       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2378       return NULL;
2379   }
2380
2381   return api;
2382 }
2383
2384
2385
2386
2387 /* end of plugin_transport_http_server.c */