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