fix type issue
[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   unsigned long int ctag;
880
881   /* URL parsing
882    * URL is valid if it is in the form [prefix with (multiple) '/'][peerid[103];tag]*/
883
884   if (NULL == url)
885   {
886       GNUNET_break (0);
887       return GNUNET_SYSERR;
888   }
889   /* convert tag */
890
891   /* find separator */
892   separator = strrchr (url, ';');
893
894   if (NULL == separator)
895   {
896       if (debug) GNUNET_break (0);
897       return GNUNET_SYSERR;
898   }
899   tag_start = separator + 1;
900
901   if (strlen (tag_start) == 0)
902   {
903     /* No tag after separator */
904     if (debug) GNUNET_break (0);
905     return GNUNET_SYSERR;
906   }
907   ctag = strtoul (tag_start, &tag_end, 10);
908   if (ctag == 0)
909   {
910     /* tag == 0 , invalid */
911     if (debug) GNUNET_break (0);
912     return GNUNET_SYSERR;
913   }
914   if ((ctag == ULONG_MAX) && (ERANGE == errno))
915   {
916     /* out of range: > ULONG_MAX */
917     if (debug) GNUNET_break (0);
918     return GNUNET_SYSERR;
919   }
920   if (ctag > UINT32_MAX)
921   {
922     /* out of range: > UINT32_MAX */
923     if (debug) GNUNET_break (0);
924     return GNUNET_SYSERR;
925   }
926   (*tag) = (uint32_t) ctag;
927   if (NULL == tag_end)
928   {
929       /* no char after tag */
930       if (debug) GNUNET_break (0);
931       return GNUNET_SYSERR;
932   }
933   if (url[strlen(url)] != tag_end[0])
934   {
935       /* there are more not converted chars after tag */
936       if (debug) GNUNET_break (0);
937       return GNUNET_SYSERR;
938   }
939   if (debug)
940     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
941        "Found tag `%u' in url\n", (*tag));
942
943   /* convert peer id */
944   target_start = strrchr (url, '/');
945   if (NULL == target_start)
946   {
947       /* no leading '/' */
948       target_start = (char *) url;
949   }
950   target_start++;
951   hash_length = separator - target_start;
952   if (hash_length != plugin->peer_id_length)
953   {
954       /* no char after tag */
955       if (debug) GNUNET_break (0);
956       return GNUNET_SYSERR;
957   }
958   memcpy (hash, target_start, hash_length);
959   hash[hash_length] = '\0';
960
961   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target->hashPubKey)))
962   {
963       /* hash conversion failed */
964       if (debug) GNUNET_break (0);
965       return GNUNET_SYSERR;
966   }
967
968   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
969      "Found target `%s' in url\n", GNUNET_h2s_full(&target->hashPubKey));
970   return GNUNET_OK;
971 }
972
973
974 /**
975  * Lookup a mhd connection and create one if none is found
976  *
977  * @param plugin the plugin handle
978  * @param mhd_connection the incoming mhd_connection
979  * @param url incoming requested URL
980  * @param method PUT or GET
981  * @return the server connecetion
982  */
983 static struct ServerConnection *
984 server_lookup_connection (struct HTTP_Server_Plugin *plugin,
985                        struct MHD_Connection *mhd_connection, const char *url,
986                        const char *method)
987 {
988   struct Session *s = NULL;
989   struct ServerConnection *sc = NULL;
990   const union MHD_ConnectionInfo *conn_info;
991   struct GNUNET_ATS_Information ats;
992
993   char *addr;
994   size_t addr_len;
995
996   struct GNUNET_PeerIdentity target;
997   uint32_t tag = 0;
998   int direction = GNUNET_SYSERR;
999   int to;
1000
1001   conn_info = MHD_get_connection_info (mhd_connection,
1002                                        MHD_CONNECTION_INFO_CLIENT_ADDRESS);
1003   if ((conn_info->client_addr->sa_family != AF_INET) &&
1004       (conn_info->client_addr->sa_family != AF_INET6))
1005     return NULL;
1006   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1007                    "New %s connection from %s\n", method, url);
1008
1009   if (GNUNET_SYSERR == server_parse_url (plugin, url, &target, &tag))
1010   {
1011       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1012                        "Invalid url %s\n", url);
1013       return NULL;
1014   }
1015   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
1016     direction = _RECEIVE;
1017   else if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
1018     direction = _SEND;
1019   else
1020   {
1021     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1022                      "Invalid method %s connection from %s\n", method, url);
1023     return NULL;
1024   }
1025
1026   plugin->cur_connections++;
1027   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1028                    "New %s connection from %s with tag %u (%u of %u)\n",
1029                    method,
1030                    GNUNET_i2s (&target), tag,
1031                    plugin->cur_connections, plugin->max_connections);
1032   /* find duplicate session */
1033   s = plugin->head;
1034   while (s != NULL)
1035   {
1036     if ((0 == memcmp (&s->target, &target, sizeof (struct GNUNET_PeerIdentity))) &&
1037         (s->tag == tag))
1038       break;
1039     s = s->next;
1040   }
1041   if (s != NULL)
1042   {
1043     if ((_RECEIVE == direction) && (NULL != s->server_recv))
1044     {
1045       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1046                        "Duplicate PUT connection from `%s' tag %u, dismissing new connection\n",
1047                        GNUNET_i2s (&target),
1048                        tag);
1049       return NULL;
1050
1051     }
1052     if ((_SEND == direction) && (NULL != s->server_send))
1053     {
1054         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1055                          "Duplicate GET connection from `%s' tag %u, dismissing new connection\n",
1056                          GNUNET_i2s (&target),
1057                          tag);
1058         return NULL;
1059     }
1060   }
1061   else
1062   {
1063     /* create new session */
1064     switch (conn_info->client_addr->sa_family)
1065     {
1066     case (AF_INET):
1067       addr = http_common_address_from_socket (plugin->protocol, conn_info->client_addr, sizeof (struct sockaddr_in));
1068       addr_len = http_common_address_get_size (addr);
1069       ats = plugin->env->get_address_type (plugin->env->cls, conn_info->client_addr, sizeof (struct sockaddr_in));
1070       break;
1071     case (AF_INET6):
1072       addr = http_common_address_from_socket (plugin->protocol, conn_info->client_addr, sizeof (struct sockaddr_in6));
1073       addr_len = http_common_address_get_size (addr);
1074       ats = plugin->env->get_address_type (plugin->env->cls, conn_info->client_addr, sizeof (struct sockaddr_in6));
1075       break;
1076     default:
1077       GNUNET_break (0);
1078       return NULL;
1079     }
1080     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1081                      "Creating new session for peer `%s' connecting from `%s'\n",
1082                      GNUNET_i2s (&target),
1083                      http_common_plugin_address_to_string (NULL, addr, addr_len));
1084
1085     s = GNUNET_malloc (sizeof (struct Session));
1086     memcpy (&s->target, &target, sizeof (struct GNUNET_PeerIdentity));
1087     s->plugin = plugin;
1088     s->addr = addr;
1089     s->addrlen = addr_len;
1090     s->ats_address_network_type = ats.value;
1091     s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS;
1092     s->tag = tag;
1093     s->server_recv = NULL;
1094     s->server_send = NULL;
1095     s->session_passed = GNUNET_NO;
1096     s->session_ended = GNUNET_NO;
1097     server_start_session_timeout(s);
1098     GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
1099   }
1100   sc = GNUNET_malloc (sizeof (struct ServerConnection));
1101   if (conn_info->client_addr->sa_family == AF_INET)
1102     sc->mhd_daemon = plugin->server_v4;
1103   if (conn_info->client_addr->sa_family == AF_INET6)
1104     sc->mhd_daemon = plugin->server_v6;
1105   sc->mhd_conn = mhd_connection;
1106   sc->direction = direction;
1107   sc->connected = GNUNET_NO;
1108   sc->session = s;
1109   if (direction == _SEND)
1110     s->server_send = sc;
1111   if (direction == _RECEIVE)
1112     s->server_recv = sc;
1113 #if MHD_VERSION >= 0x00090E00
1114   if ((NULL == s->server_recv) || (NULL == s->server_send))
1115   {
1116     to = (HTTP_SERVER_NOT_VALIDATED_TIMEOUT.rel_value / 1000);
1117     MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to);
1118     server_reschedule (plugin, sc->mhd_daemon, GNUNET_NO);
1119   }
1120   else
1121   {
1122     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1123                      "Session %p for peer `%s' fully connected\n",
1124                      s, GNUNET_i2s (&target));
1125     to = (SERVER_SESSION_TIMEOUT.rel_value / 1000);
1126     server_mhd_connection_timeout (plugin, s, to);
1127   }
1128
1129   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1130                    "Setting timeout for %p to %u sec.\n", sc, to);
1131 #endif
1132   return sc;
1133 }
1134
1135
1136 /**
1137  * Lookup a session for a server connection
1138  *
1139  * @param plugin the plugin
1140  * @param sc the server connection
1141  * @return the session found or NULL
1142  */
1143 static struct Session *
1144 server_lookup_session (struct HTTP_Server_Plugin *plugin,
1145                        struct ServerConnection * sc)
1146 {
1147   struct Session *s;
1148
1149   for (s = plugin->head; NULL != s; s = s->next)
1150     if ((s->server_recv == sc) || (s->server_send == sc))
1151       return s;
1152   return NULL;
1153 }
1154
1155 int
1156 server_exist_session (struct HTTP_Server_Plugin *plugin, struct Session *s)
1157 {
1158   struct Session * head;
1159
1160   GNUNET_assert (NULL != plugin);
1161   GNUNET_assert (NULL != s);
1162
1163   for (head = plugin->head; head != NULL; head = head->next)
1164   {
1165     if (head == s)
1166       return GNUNET_YES;
1167   }
1168   return GNUNET_NO;
1169 }
1170
1171
1172 /**
1173  * Callback called by MHD when it needs data to send
1174  *
1175  * @param cls current session
1176  * @param pos position in buffer
1177  * @param buf the buffer to write data to
1178  * @param max max number of bytes available in buffer
1179  * @return bytes written to buffer
1180  */
1181 static ssize_t
1182 server_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
1183 {
1184   struct Session *s = cls;
1185   ssize_t bytes_read = 0;
1186   struct HTTP_Message *msg;
1187   char *stat_txt;
1188
1189   GNUNET_assert (NULL != p);
1190   if (GNUNET_NO == server_exist_session (p, s))
1191     return 0;
1192   msg = s->msg_head;
1193   if (NULL != msg)
1194   {
1195     /* sending */
1196     bytes_read = GNUNET_MIN (msg->size - msg->pos,
1197                              max);
1198     memcpy (buf, &msg->buf[msg->pos], bytes_read);
1199     msg->pos += bytes_read;
1200
1201     /* removing message */
1202     if (msg->pos == msg->size)
1203     {
1204       GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
1205       if (NULL != msg->transmit_cont)
1206         msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
1207       GNUNET_free (msg);
1208     }
1209   }
1210   if (0 < bytes_read)
1211   {
1212     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
1213                    "Sent %u bytes to peer `%s' with session %p \n", bytes_read, GNUNET_i2s (&s->target), s);
1214     GNUNET_asprintf (&stat_txt, "# bytes currently in %s_server buffers", p->protocol);
1215     GNUNET_STATISTICS_update (p->env->stats,
1216                               stat_txt, -bytes_read, GNUNET_NO);
1217     GNUNET_free (stat_txt);
1218     GNUNET_asprintf (&stat_txt, "# bytes transmitted via %s_server", p->protocol);
1219     GNUNET_STATISTICS_update (p->env->stats,
1220                               stat_txt, bytes_read, GNUNET_NO);
1221     GNUNET_free (stat_txt);
1222   }
1223   return bytes_read;
1224 }
1225
1226
1227 /**
1228  * Callback called by MessageStreamTokenizer when a message has arrived
1229  *
1230  * @param cls current session as closure
1231  * @param client client
1232  * @param message the message to be forwarded to transport service
1233  * @return GNUNET_OK
1234  */
1235 static int
1236 server_receive_mst_cb (void *cls, void *client,
1237                        const struct GNUNET_MessageHeader *message)
1238 {
1239   struct Session *s = cls;
1240   struct GNUNET_ATS_Information atsi[2];
1241   struct GNUNET_TIME_Relative delay;
1242   char *stat_txt;
1243
1244   GNUNET_assert (NULL != p);
1245   if (GNUNET_NO == server_exist_session(p, s))
1246     return GNUNET_OK;
1247
1248   struct HTTP_Server_Plugin *plugin = s->plugin;
1249
1250   atsi[0].type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
1251   atsi[0].value = htonl (1);
1252   atsi[1].type = htonl (GNUNET_ATS_NETWORK_TYPE);
1253   atsi[1].value = s->ats_address_network_type;
1254   GNUNET_break (s->ats_address_network_type != ntohl (GNUNET_ATS_NET_UNSPECIFIED));
1255
1256
1257   delay = plugin->env->receive (plugin->env->cls,
1258                                 &s->target,
1259                                 message,
1260                                 (const struct GNUNET_ATS_Information *) &atsi, 2,
1261                                 s, s->addr, s->addrlen);
1262
1263   GNUNET_asprintf (&stat_txt, "# bytes received via %s_server", plugin->protocol);
1264   GNUNET_STATISTICS_update (plugin->env->stats,
1265                             stat_txt, ntohs (message->size), GNUNET_NO);
1266   GNUNET_free (stat_txt);
1267
1268   s->session_passed = GNUNET_YES;
1269   s->next_receive = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay);
1270   if (delay.rel_value > 0)
1271   {
1272     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1273                      "Peer `%s' address `%s' next read delayed for %llu ms\n",
1274                      GNUNET_i2s (&s->target),
1275                      http_common_plugin_address_to_string (NULL, s->addr, s->addrlen),
1276                      delay);
1277   }
1278   server_reschedule_session_timeout (s);
1279   return GNUNET_OK;
1280 }
1281
1282
1283 /**
1284  * MHD callback for a new incoming connection
1285  *
1286  * @param cls the plugin handle
1287  * @param mhd_connection the mhd connection
1288  * @param url the requested URL
1289  * @param method GET or PUT
1290  * @param version HTTP version
1291  * @param upload_data upload data
1292  * @param upload_data_size sizeof upload data
1293  * @param httpSessionCache the session cache to remember the connection
1294  * @return MHD_YES if connection is accepted, MHD_NO on reject
1295  */
1296 static int
1297 server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
1298                   const char *url, const char *method, const char *version,
1299                   const char *upload_data, size_t * upload_data_size,
1300                   void **httpSessionCache)
1301 {
1302   struct HTTP_Server_Plugin *plugin = cls;
1303   int res = MHD_YES;
1304
1305   struct ServerConnection *sc = *httpSessionCache;
1306   struct Session *s;
1307   struct MHD_Response *response;
1308
1309   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1310                    _("Access from connection %p (%u of %u) for `%s' `%s' url `%s' with upload data size %u\n"),
1311                    sc,
1312                    plugin->cur_connections, plugin->max_connections,
1313                    method, version, url, (*upload_data_size));
1314
1315   GNUNET_assert (cls != NULL);
1316   if (sc == NULL)
1317   {
1318     /* new connection */
1319     sc = server_lookup_connection (plugin, mhd_connection, url, method);
1320     if (sc != NULL)
1321     {
1322       (*httpSessionCache) = sc;
1323     }
1324     else
1325     {
1326       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
1327       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
1328       MHD_destroy_response (response);
1329       return res;
1330     }
1331   }
1332   else
1333   {
1334     /* 'old' connection */
1335     if (NULL == server_lookup_session (plugin, sc))
1336     {
1337       /* Session was already disconnected */
1338       return MHD_NO;
1339     }
1340   }
1341
1342   /* existing connection */
1343   sc = (*httpSessionCache);
1344   s = sc->session;
1345   GNUNET_assert (NULL != s);
1346   /* connection is to be disconnected */
1347   if (sc->disconnect == GNUNET_YES)
1348   {
1349     /* Sent HTTP/1.1: 200 OK as response */
1350     response = MHD_create_response_from_data (strlen ("Thank you!"),
1351                                        "Thank you!",
1352                                        MHD_NO, MHD_NO);
1353     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1354     MHD_destroy_response (response);
1355     return MHD_YES;
1356   }
1357   GNUNET_assert (s != NULL);
1358
1359   if (sc->direction == _SEND)
1360   {
1361     response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
1362                                                   32 * 1024,
1363                                                   &server_send_callback, s,
1364                                                   NULL);
1365     MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1366     MHD_destroy_response (response);
1367     return MHD_YES;
1368   }
1369   if (sc->direction == _RECEIVE)
1370   {
1371     if ((*upload_data_size == 0) && (sc->connected == GNUNET_NO))
1372     {
1373       /* (*upload_data_size == 0) first callback when header are passed */
1374       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1375                        "Session %p / Connection %p: Peer `%s' PUT on address `%s' connected\n",
1376                        s, sc,
1377                        GNUNET_i2s (&s->target),
1378                        http_common_plugin_address_to_string (NULL,
1379                                                              s->addr,
1380                                                              s->addrlen));
1381       sc->connected = GNUNET_YES;
1382       return MHD_YES;
1383     }
1384     else if ((*upload_data_size == 0) && (sc->connected == GNUNET_YES))
1385     {
1386       /* (*upload_data_size == 0) when upload is complete */
1387       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1388                        "Session %p / Connection %p: Peer `%s' PUT on address `%s' finished upload\n",
1389                        s, sc,
1390                        GNUNET_i2s (&s->target),
1391                        http_common_plugin_address_to_string (NULL,
1392                                                              s->addr,
1393                                                              s->addrlen));
1394       sc->connected = GNUNET_NO;
1395       /* Sent HTTP/1.1: 200 OK as PUT Response\ */
1396       response = MHD_create_response_from_data (strlen ("Thank you!"),
1397                                          "Thank you!",
1398                                          MHD_NO, MHD_NO);
1399       res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1400       MHD_destroy_response (response);
1401       return MHD_YES;
1402     }
1403     else if ((*upload_data_size > 0) && (sc->connected == GNUNET_YES))
1404     {
1405       /* (*upload_data_size > 0) for every segment received */
1406       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1407                        "Session %p / Connection %p: Peer `%s' PUT on address `%s' received %u bytes\n",
1408                        s, sc,
1409                        GNUNET_i2s (&s->target),
1410                        http_common_plugin_address_to_string (NULL,
1411                                                              s->addr,
1412                                                              s->addrlen),
1413                        *upload_data_size);
1414       struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1415
1416       if ((s->next_receive.abs_value <= now.abs_value))
1417       {
1418         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1419                          "PUT with %u bytes forwarded to MST\n",
1420                          *upload_data_size);
1421         if (s->msg_tk == NULL)
1422         {
1423           s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s);
1424         }
1425             GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data,
1426                                        *upload_data_size, GNUNET_NO, GNUNET_NO);
1427 #if MHD_VERSION >= 0x00090E00
1428         server_mhd_connection_timeout (plugin, s, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
1429 #endif
1430         (*upload_data_size) = 0;
1431       }
1432       else
1433       {
1434         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1435                     "Session %p / Connection %p: no inbound bandwidth available! Next read was delayed by %llu ms\n",
1436                     s, sc, now.abs_value - s->next_receive.abs_value);
1437       }
1438       return MHD_YES;
1439     }
1440     else
1441     {
1442       GNUNET_break (0);
1443       return MHD_NO;
1444     }
1445   }
1446   return res;
1447 }
1448
1449
1450 /**
1451  * Callback from MHD when a connection disconnects
1452  *
1453  * @param cls closure
1454  * @param connection the disconnected MHD connection
1455  * @param httpSessionCache the pointer to distinguish
1456  */
1457 static void
1458 server_disconnect_cb (void *cls, struct MHD_Connection *connection,
1459                       void **httpSessionCache)
1460 {
1461   struct ServerConnection *sc = *httpSessionCache;
1462   struct Session *s = NULL;
1463   struct Session *t = NULL;
1464   struct HTTP_Server_Plugin *plugin = NULL;
1465
1466   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, p->name,
1467                    "Disconnect for connection %p \n", sc);
1468
1469   if (sc == NULL)
1470     return;
1471
1472   if (NULL == (s = server_lookup_session (p, sc)))
1473     return;
1474
1475   GNUNET_assert (NULL != p);
1476   for (t = p->head; t != NULL; t = t->next)
1477   {
1478     if (t == s)
1479       break;
1480   }
1481   if (NULL == t)
1482     return;
1483
1484   plugin = s->plugin;
1485   if (sc->direction == _SEND)
1486   {
1487
1488     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1489                      "Peer `%s' connection  %p, GET on address `%s' disconnected\n",
1490                      GNUNET_i2s (&s->target), s->server_send,
1491                      http_common_plugin_address_to_string (NULL, s->addr, s->addrlen));
1492     s->server_send = NULL;
1493     if (NULL != (s->server_recv))
1494     {
1495       s->server_recv->disconnect = GNUNET_YES;
1496       GNUNET_assert (NULL != s->server_recv->mhd_conn);
1497 #if MHD_VERSION >= 0x00090E00
1498       MHD_set_connection_option (s->server_recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
1499                                  1);
1500 #endif
1501       server_reschedule (plugin, s->server_recv->mhd_daemon, GNUNET_NO);
1502     }
1503   }
1504   if (sc->direction == _RECEIVE)
1505   {
1506     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1507                      "Peer `%s' connection %p PUT on address `%s' disconnected\n",
1508                      GNUNET_i2s (&s->target), s->server_recv,
1509                      http_common_plugin_address_to_string (NULL, s->addr, s->addrlen));
1510     s->server_recv = NULL;
1511     /* Do not terminate session when PUT disconnects
1512     if (NULL != (s->server_send))
1513     {
1514         s->server_send->disconnect = GNUNET_YES;
1515       GNUNET_assert (NULL != s->server_send->mhd_conn);
1516 #if MHD_VERSION >= 0x00090E00
1517       MHD_set_connection_option (s->server_send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
1518                                  1);
1519 #endif
1520       server_reschedule (plugin, s->server_send->mhd_daemon, GNUNET_NO);
1521     }*/
1522     if (s->msg_tk != NULL)
1523     {
1524       GNUNET_SERVER_mst_destroy (s->msg_tk);
1525       s->msg_tk = NULL;
1526     }
1527   }
1528
1529   GNUNET_free (sc);
1530   plugin->cur_connections--;
1531
1532   if ((s->server_send == NULL) && (s->server_recv == NULL))
1533   {
1534     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1535                      "Peer `%s' on address `%s' disconnected\n",
1536                      GNUNET_i2s (&s->target),
1537                      http_common_plugin_address_to_string (NULL, s->addr, s->addrlen));
1538
1539     if ((GNUNET_YES == s->session_passed) && (GNUNET_NO == s->session_ended))
1540     {
1541         /* Notify transport immediately that this session is invalid */
1542         s->session_ended = GNUNET_YES;
1543         plugin->env->session_end (plugin->env->cls, &s->target, s);
1544     }
1545     server_delete_session (s);
1546   }
1547
1548 }
1549
1550
1551 /**
1552  * Check if incoming connection is accepted.
1553
1554  * @param cls plugin as closure
1555  * @param addr address of incoming connection
1556  * @param addr_len address length of incoming connection
1557  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
1558  *
1559  */
1560 static int
1561 server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
1562 {
1563   struct HTTP_Server_Plugin *plugin = cls;
1564
1565   if (plugin->cur_connections <= plugin->max_connections)
1566   {
1567     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1568                      _("Accepting connection (%u of %u) from `%s'\n"),
1569                      plugin->cur_connections, plugin->max_connections,
1570                      GNUNET_a2s (addr, addr_len));
1571     return MHD_YES;
1572   }
1573   else
1574   {
1575     GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1576                      _("Server reached maximum number connections (%u), rejecting new connection\n"),
1577                      plugin->max_connections);
1578     return MHD_NO;
1579   }
1580 }
1581
1582 static void
1583 server_log (void *arg, const char *fmt, va_list ap)
1584 {
1585   char text[1024];
1586
1587   vsnprintf (text, sizeof (text), fmt, ap);
1588   va_end (ap);
1589   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Server: %s\n", text);
1590 }
1591
1592
1593 /**
1594  * Call MHD IPv4 to process pending requests and then go back
1595  * and schedule the next run.
1596  * @param cls plugin as closure
1597  * @param tc task context
1598  */
1599 static void
1600 server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1601 {
1602   struct HTTP_Server_Plugin *plugin = cls;
1603
1604   GNUNET_assert (cls != NULL);
1605
1606   plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1607   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1608     return;
1609 #if 0
1610   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1611                    "Running IPv4 server\n");
1612 #endif
1613   plugin->server_v4_immediately = GNUNET_NO;
1614   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4));
1615   server_reschedule (plugin, plugin->server_v4, GNUNET_NO);
1616 }
1617
1618
1619 /**
1620  * Call MHD IPv6 to process pending requests and then go back
1621  * and schedule the next run.
1622  * @param cls plugin as closure
1623  * @param tc task context
1624  */
1625 static void
1626 server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1627 {
1628   struct HTTP_Server_Plugin *plugin = cls;
1629
1630   GNUNET_assert (cls != NULL);
1631   plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1632   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1633     return;
1634 #if 0
1635   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1636                    "Running IPv6 server\n");
1637 #endif
1638   plugin->server_v6_immediately = GNUNET_NO;
1639   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6));
1640   server_reschedule (plugin, plugin->server_v6, GNUNET_NO);
1641 }
1642
1643
1644 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
1645
1646 /**
1647  * Function that queries MHD's select sets and
1648  * starts the task waiting for them.
1649  *
1650  * @param plugin plugin
1651  * @param daemon_handle the MHD daemon handle
1652  * @return gnunet task identifier
1653  */
1654 static GNUNET_SCHEDULER_TaskIdentifier
1655 server_schedule (struct HTTP_Server_Plugin *plugin,
1656                  struct MHD_Daemon *daemon_handle,
1657                  int now)
1658 {
1659   GNUNET_SCHEDULER_TaskIdentifier ret;
1660   fd_set rs;
1661   fd_set ws;
1662   fd_set es;
1663   struct GNUNET_NETWORK_FDSet *wrs;
1664   struct GNUNET_NETWORK_FDSet *wws;
1665   struct GNUNET_NETWORK_FDSet *wes;
1666   int max;
1667   UNSIGNED_MHD_LONG_LONG timeout;
1668   static unsigned long long last_timeout = 0;
1669   int haveto;
1670
1671   struct GNUNET_TIME_Relative tv;
1672
1673   if (GNUNET_YES == plugin->in_shutdown)
1674     return GNUNET_SCHEDULER_NO_TASK;
1675
1676   ret = GNUNET_SCHEDULER_NO_TASK;
1677   FD_ZERO (&rs);
1678   FD_ZERO (&ws);
1679   FD_ZERO (&es);
1680   wrs = GNUNET_NETWORK_fdset_create ();
1681   wes = GNUNET_NETWORK_fdset_create ();
1682   wws = GNUNET_NETWORK_fdset_create ();
1683   max = -1;
1684   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
1685   haveto = MHD_get_timeout (daemon_handle, &timeout);
1686   if (haveto == MHD_YES)
1687   {
1688     if (timeout != last_timeout)
1689     {
1690
1691       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1692                        "SELECT Timeout changed from %llu to %llu\n",
1693                        last_timeout, timeout);
1694       last_timeout = timeout;
1695     }
1696     if (timeout <= GNUNET_TIME_UNIT_SECONDS.rel_value)
1697       tv.rel_value = (uint64_t) timeout;
1698     else
1699       tv = GNUNET_TIME_UNIT_SECONDS;
1700   }
1701   else
1702     tv = GNUNET_TIME_UNIT_SECONDS;
1703   /* Force immediate run, since we have outbound data to send */
1704   if (now == GNUNET_YES)
1705     tv = GNUNET_TIME_UNIT_MILLISECONDS;
1706   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1707   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1708   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1709
1710   if (daemon_handle == plugin->server_v4)
1711   {
1712     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1713     {
1714       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1715       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1716     }
1717 #if 0
1718     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1719                      "Scheduling IPv4 server task in %llu ms\n", tv);
1720 #endif
1721     ret =
1722         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1723                                      tv, wrs, wws,
1724                                      &server_v4_run, plugin);
1725   }
1726   if (daemon_handle == plugin->server_v6)
1727   {
1728     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1729     {
1730       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1731       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1732     }
1733 #if 0
1734     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1735                      "Scheduling IPv6 server task in %llu ms\n", tv);
1736 #endif
1737     ret =
1738         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1739                                      tv, wrs, wws,
1740                                      &server_v6_run, plugin);
1741   }
1742   GNUNET_NETWORK_fdset_destroy (wrs);
1743   GNUNET_NETWORK_fdset_destroy (wws);
1744   GNUNET_NETWORK_fdset_destroy (wes);
1745   return ret;
1746 }
1747
1748
1749 #if BUILD_HTTPS
1750 /**
1751  * Load ssl certificate from file
1752  *
1753  * @param file filename
1754  * @return content of the file
1755  */
1756 static char *
1757 server_load_file (const char *file)
1758 {
1759   struct GNUNET_DISK_FileHandle *gn_file;
1760   uint64_t fsize;
1761   char *text = NULL;
1762
1763   if (GNUNET_OK != GNUNET_DISK_file_size (file,
1764       &fsize, GNUNET_NO, GNUNET_YES))
1765     return NULL;
1766   text = GNUNET_malloc (fsize + 1);
1767   gn_file =
1768       GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
1769                              GNUNET_DISK_PERM_USER_READ);
1770   if (gn_file == NULL)
1771   {
1772     GNUNET_free (text);
1773     return NULL;
1774   }
1775   if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fsize))
1776   {
1777     GNUNET_free (text);
1778     GNUNET_DISK_file_close (gn_file);
1779     return NULL;
1780   }
1781   text[fsize] = '\0';
1782   GNUNET_DISK_file_close (gn_file);
1783   return text;
1784 }
1785 #endif
1786
1787
1788 #if BUILD_HTTPS
1789 /**
1790  * Load ssl certificate
1791  *
1792  * @param plugin the plugin
1793  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1794  */
1795 static int
1796 server_load_certificate (struct HTTP_Server_Plugin *plugin)
1797 {
1798   int res = GNUNET_OK;
1799
1800   char *sh;
1801   char *key_file;
1802   char *cert_file;
1803
1804   /* Get crypto init string from config
1805    * If not present just use default values */
1806
1807   if (GNUNET_OK !=
1808                  GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1809                                                         "PATHS",
1810                                                         "SERVICEHOME",
1811                                                         &sh))
1812   {
1813       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1814                        "Failed to get servicehome!\n");
1815       return GNUNET_SYSERR;
1816   }
1817
1818
1819   if (GNUNET_OK ==
1820                  GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1821                                                         plugin->name,
1822                                                         "CRYPTO_INIT",
1823                                                         &plugin->crypto_init))
1824       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1825                        "Using crypto init string `%s'\n",
1826                        plugin->crypto_init);
1827   else
1828     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1829                      "Using default crypto init string \n");
1830
1831   if (GNUNET_OK !=
1832       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
1833                                                "KEY_FILE", &key_file))
1834   {
1835     GNUNET_break (0);
1836     GNUNET_asprintf (&key_file, "%s/%s", sh, "https_key.key");
1837   }
1838
1839
1840   if (GNUNET_OK !=
1841       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
1842                                                "CERT_FILE", &cert_file))
1843   {
1844       GNUNET_break (0);
1845     GNUNET_asprintf (&cert_file, "%s/%s", sh, "https_cert.crt");
1846   }
1847   GNUNET_free (sh);
1848   /* read key & certificates from file */
1849   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1850               "Trying to loading TLS certificate from key-file `%s' cert-file`%s'\n",
1851               key_file, cert_file);
1852
1853   plugin->key = server_load_file (key_file);
1854   plugin->cert = server_load_file (cert_file);
1855
1856   if ((plugin->key == NULL) || (plugin->cert == NULL))
1857   {
1858     struct GNUNET_OS_Process *cert_creation;
1859
1860     GNUNET_free_non_null (plugin->key);
1861     plugin->key = NULL;
1862     GNUNET_free_non_null (plugin->cert);
1863     plugin->cert = NULL;
1864
1865     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1866                 "No usable TLS certificate found, creating certificate\n");
1867     errno = 0;
1868     cert_creation =
1869         GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL,
1870                                  "gnunet-transport-certificate-creation",
1871                                  "gnunet-transport-certificate-creation",
1872                                  key_file, cert_file, NULL);
1873     if (cert_creation == NULL)
1874     {
1875       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1876                        _
1877                        ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
1878       GNUNET_free (key_file);
1879       GNUNET_free (cert_file);
1880
1881       GNUNET_free_non_null (plugin->key);
1882       plugin->key = NULL;
1883       GNUNET_free_non_null (plugin->cert);
1884       plugin->cert = NULL;
1885       GNUNET_free_non_null (plugin->crypto_init);
1886       plugin->crypto_init = NULL;
1887
1888       return GNUNET_SYSERR;
1889     }
1890     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation));
1891     GNUNET_OS_process_destroy (cert_creation);
1892
1893     plugin->key = server_load_file (key_file);
1894     plugin->cert = server_load_file (cert_file);
1895   }
1896
1897   if ((plugin->key == NULL) || (plugin->cert == NULL))
1898   {
1899     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1900                      _
1901                      ("No usable TLS certificate found and creating one failed!\n"),
1902                      "transport-https");
1903     GNUNET_free (key_file);
1904     GNUNET_free (cert_file);
1905
1906     GNUNET_free_non_null (plugin->key);
1907     plugin->key = NULL;
1908     GNUNET_free_non_null (plugin->cert);
1909     plugin->cert = NULL;
1910     GNUNET_free_non_null (plugin->crypto_init);
1911     plugin->crypto_init = NULL;
1912
1913     return GNUNET_SYSERR;
1914   }
1915   GNUNET_free (key_file);
1916   GNUNET_free (cert_file);
1917   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
1918   return res;
1919 }
1920 #endif
1921
1922
1923 /**
1924  * Start the HTTP server
1925  *
1926  * @param plugin the plugin handle
1927  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1928  */
1929 static int
1930 server_start (struct HTTP_Server_Plugin *plugin)
1931 {
1932   unsigned int timeout;
1933   GNUNET_assert (NULL != plugin);
1934
1935 #if BUILD_HTTPS
1936   if (GNUNET_SYSERR == server_load_certificate (plugin))
1937   {
1938     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1939                      "Could not load or create server certificate! Loading plugin failed!\n");
1940     return GNUNET_SYSERR;
1941   }
1942 #endif
1943
1944
1945 #if MHD_VERSION >= 0x00090E00
1946   timeout = HTTP_SERVER_NOT_VALIDATED_TIMEOUT.rel_value / 1000;
1947   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1948                    "MHD can set timeout per connection! Default time out %u sec.\n",
1949                    timeout);
1950 #else
1951   timeout = SERVER_SESSION_TIMEOUT.rel_value / 1000;
1952   GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1953                    "MHD cannot set timeout per connection! Default time out %u sec.\n",
1954                    timeout);
1955 #endif
1956   plugin->server_v4 = NULL;
1957   if (plugin->use_ipv4 == GNUNET_YES)
1958   {
1959     plugin->server_v4 = MHD_start_daemon (
1960 #if VERBOSE_SERVER
1961                                            MHD_USE_DEBUG |
1962 #endif
1963 #if BUILD_HTTPS
1964                                            MHD_USE_SSL |
1965 #endif
1966                                            MHD_NO_FLAG, plugin->port,
1967                                            &server_accept_cb, plugin,
1968                                            &server_access_cb, plugin,
1969                                            MHD_OPTION_SOCK_ADDR,
1970                                            (struct sockaddr_in *)
1971                                            plugin->server_addr_v4,
1972                                            MHD_OPTION_CONNECTION_LIMIT,
1973                                            (unsigned int)
1974                                            plugin->max_connections,
1975 #if BUILD_HTTPS
1976                                            MHD_OPTION_HTTPS_PRIORITIES,
1977                                            plugin->crypto_init,
1978                                            MHD_OPTION_HTTPS_MEM_KEY,
1979                                            plugin->key,
1980                                            MHD_OPTION_HTTPS_MEM_CERT,
1981                                            plugin->cert,
1982 #endif
1983                                            MHD_OPTION_CONNECTION_TIMEOUT,
1984                                            timeout,
1985                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1986                                            (size_t) (2 *
1987                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1988                                            MHD_OPTION_NOTIFY_COMPLETED,
1989                                            &server_disconnect_cb, plugin,
1990                                            MHD_OPTION_EXTERNAL_LOGGER,
1991                                            server_log, NULL, MHD_OPTION_END);
1992   }
1993   plugin->server_v6 = NULL;
1994   if (plugin->use_ipv6 == GNUNET_YES)
1995   {
1996     plugin->server_v6 = MHD_start_daemon (
1997 #if VERBOSE_SERVER
1998                                            MHD_USE_DEBUG |
1999 #endif
2000 #if BUILD_HTTPS
2001                                            MHD_USE_SSL |
2002 #endif
2003                                            MHD_USE_IPv6, plugin->port,
2004                                            &server_accept_cb, plugin,
2005                                            &server_access_cb, plugin,
2006                                            MHD_OPTION_SOCK_ADDR,
2007                                            (struct sockaddr_in6 *)
2008                                            plugin->server_addr_v6,
2009                                            MHD_OPTION_CONNECTION_LIMIT,
2010                                            (unsigned int)
2011                                            plugin->max_connections,
2012 #if BUILD_HTTPS
2013                                            MHD_OPTION_HTTPS_PRIORITIES,
2014                                            plugin->crypto_init,
2015                                            MHD_OPTION_HTTPS_MEM_KEY,
2016                                            plugin->key,
2017                                            MHD_OPTION_HTTPS_MEM_CERT,
2018                                            plugin->cert,
2019 #endif
2020                                            MHD_OPTION_CONNECTION_TIMEOUT,
2021                                            timeout,
2022                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
2023                                            (size_t) (2 *
2024                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
2025                                            MHD_OPTION_NOTIFY_COMPLETED,
2026                                            &server_disconnect_cb, plugin,
2027                                            MHD_OPTION_EXTERNAL_LOGGER,
2028                                            server_log, NULL, MHD_OPTION_END);
2029
2030   }
2031
2032   if ((plugin->use_ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL))
2033   {
2034     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2035                      "Failed to start %s IPv4 server component on port %u\n",
2036                      plugin->name, plugin->port);
2037     return GNUNET_SYSERR;
2038   }
2039   server_reschedule (plugin, plugin->server_v4, GNUNET_NO);
2040
2041   if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL))
2042   {
2043     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2044                      "Failed to start %s IPv6 server component on port %u\n",
2045                      plugin->name, plugin->port);
2046     return GNUNET_SYSERR;
2047   }
2048   server_reschedule (plugin, plugin->server_v6, GNUNET_NO);
2049   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2050                    "%s server component started on port %u\n", plugin->name,
2051                    plugin->port);
2052   return GNUNET_OK;
2053 }
2054
2055
2056 void
2057 server_stop (struct HTTP_Server_Plugin *plugin)
2058 {
2059   if (plugin->server_v4 != NULL)
2060   {
2061     MHD_stop_daemon (plugin->server_v4);
2062     plugin->server_v4 = NULL;
2063   }
2064   if ( plugin->server_v6 != NULL)
2065   {
2066     MHD_stop_daemon (plugin->server_v6);
2067     plugin->server_v6 = NULL;
2068   }
2069
2070
2071   if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
2072   {
2073     GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
2074     plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
2075   }
2076
2077   if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
2078   {
2079     GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
2080     plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
2081   }
2082   p = NULL;
2083
2084 #if BUILD_HTTPS
2085   GNUNET_free_non_null (plugin->crypto_init);
2086   GNUNET_free_non_null (plugin->cert);
2087   GNUNET_free_non_null (plugin->key);
2088 #endif
2089
2090   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2091                    "%s server component stopped\n", plugin->name);
2092 }
2093
2094
2095 /**
2096  * Add an address to the server's set of addresses and notify transport
2097  *
2098  * @param cls the plugin handle
2099  * @param add_remove GNUNET_YES on add, GNUNET_NO on remove
2100  * @param addr the address
2101  * @param addrlen address length
2102  */
2103 static void
2104 server_add_address (void *cls, int add_remove, const struct sockaddr *addr,
2105                  socklen_t addrlen)
2106 {
2107   struct HTTP_Server_Plugin *plugin = cls;
2108   struct HttpAddressWrapper *w = NULL;
2109
2110   w = GNUNET_malloc (sizeof (struct HttpAddressWrapper));
2111   w->addr = http_common_address_from_socket (plugin->protocol, addr, addrlen);
2112   if (NULL == w->addr)
2113   {
2114     GNUNET_free (w);
2115     return;
2116   }
2117   w->addrlen = http_common_address_get_size (w->addr);
2118
2119   GNUNET_CONTAINER_DLL_insert(plugin->addr_head, plugin->addr_tail, w);
2120   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2121                    "Notifying transport to add address `%s'\n",
2122                    http_common_plugin_address_to_string(NULL, w->addr, w->addrlen));
2123 #if BUILD_HTTPS
2124   plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "https_client");
2125 #else
2126   plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "http_client");
2127 #endif
2128 }
2129
2130
2131 /**
2132  * Remove an address from the server's set of addresses and notify transport
2133  *
2134  * @param cls the plugin handle
2135  * @param add_remove GNUNET_YES on add, GNUNET_NO on remove
2136  * @param addr the address
2137  * @param addrlen address length
2138  */
2139 static void
2140 server_remove_address (void *cls, int add_remove, const struct sockaddr *addr,
2141                     socklen_t addrlen)
2142 {
2143   struct HTTP_Server_Plugin *plugin = cls;
2144   struct HttpAddressWrapper *w = plugin->addr_head;
2145   size_t saddr_len;
2146   void * saddr = http_common_address_from_socket (plugin->protocol, addr, addrlen);
2147   if (NULL == saddr)
2148     return;
2149   saddr_len =  http_common_address_get_size (saddr);
2150
2151   while (NULL != w)
2152   {
2153       if (GNUNET_YES == http_common_cmp_addresses(w->addr, w->addrlen, saddr, saddr_len))
2154         break;
2155       w = w->next;
2156   }
2157   GNUNET_free (saddr);
2158
2159   if (NULL == w)
2160     return;
2161
2162   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2163                    "Notifying transport to remove address `%s'\n",
2164                    http_common_plugin_address_to_string (NULL, w->addr, w->addrlen));
2165   GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w);
2166 #if BUILD_HTTPS
2167   plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "https_client");
2168 #else
2169   plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "http_client");
2170 #endif
2171   GNUNET_free (w->addr);
2172   GNUNET_free (w);
2173 }
2174
2175
2176
2177 /**
2178  * Our external IP address/port mapping has changed.
2179  *
2180  * @param cls closure, the 'struct LocalAddrList'
2181  * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
2182  *     the previous (now invalid) one
2183  * @param addr either the previous or the new public IP address
2184  * @param addrlen actual lenght of the address
2185  */
2186 static void
2187 server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr *addr,
2188                        socklen_t addrlen)
2189 {
2190   GNUNET_assert (cls != NULL);
2191   struct HTTP_Server_Plugin *plugin = cls;
2192
2193   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2194                    "NAT called to %s address `%s'\n",
2195                    (add_remove == GNUNET_NO) ? "remove" : "add",
2196                    GNUNET_a2s (addr, addrlen));
2197
2198   if (AF_INET == addr->sa_family)
2199   {
2200     struct sockaddr_in *s4 = (struct sockaddr_in *) addr;
2201
2202     if (GNUNET_NO == plugin->use_ipv4)
2203       return;
2204
2205     if ((NULL != plugin->server_addr_v4) &&
2206         (0 != memcmp (&plugin->server_addr_v4->sin_addr,
2207                       &s4->sin_addr, sizeof (struct in_addr))))
2208     {
2209         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2210                          "Skipping address `%s' (not bindto address)\n",
2211                          GNUNET_a2s (addr, addrlen));
2212       return;
2213     }
2214   }
2215
2216   if (AF_INET6 == addr->sa_family)
2217   {
2218     struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) addr;
2219     if (GNUNET_NO == plugin->use_ipv6)
2220       return;
2221
2222     if ((NULL != plugin->server_addr_v6) &&
2223         (0 != memcmp (&plugin->server_addr_v6->sin6_addr,
2224                       &s6->sin6_addr, sizeof (struct in6_addr))))
2225     {
2226         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2227                          "Skipping address `%s' (not bindto address)\n",
2228                          GNUNET_a2s (addr, addrlen));
2229         return;
2230     }
2231   }
2232
2233   switch (add_remove)
2234   {
2235   case GNUNET_YES:
2236     server_add_address (cls, add_remove, addr, addrlen);
2237     break;
2238   case GNUNET_NO:
2239     server_remove_address (cls, add_remove, addr, addrlen);
2240     break;
2241   }
2242 }
2243
2244
2245 /**
2246  * Get valid server addresses
2247  *
2248  * @param plugin the plugin handle
2249  * @param serviceName the servicename
2250  * @param cfg configuration handle
2251  * @param addrs addresses
2252  * @param addr_lens address length
2253  * @return number of addresses
2254  */
2255 static int
2256 server_get_addresses (struct HTTP_Server_Plugin *plugin,
2257                       const char *serviceName,
2258                       const struct GNUNET_CONFIGURATION_Handle *cfg,
2259                       struct sockaddr ***addrs, socklen_t ** addr_lens)
2260 {
2261   int disablev6;
2262   unsigned long long port;
2263   struct addrinfo hints;
2264   struct addrinfo *res;
2265   struct addrinfo *pos;
2266   struct addrinfo *next;
2267   unsigned int i;
2268   int resi;
2269   int ret;
2270   struct sockaddr **saddrs;
2271   socklen_t *saddrlens;
2272   char *hostname;
2273
2274   *addrs = NULL;
2275   *addr_lens = NULL;
2276
2277   disablev6 = !plugin->use_ipv6;
2278
2279   port = 0;
2280   if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "PORT"))
2281   {
2282     GNUNET_break (GNUNET_OK ==
2283                   GNUNET_CONFIGURATION_get_value_number (cfg, serviceName,
2284                                                          "PORT", &port));
2285     if (port > 65535)
2286     {
2287       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2288                   _
2289                   ("Require valid port number for service in configuration!\n"));
2290       return GNUNET_SYSERR;
2291     }
2292   }
2293   if (0 == port)
2294   {
2295     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name,
2296                      "Starting in listen only mode\n");
2297     return -1; /* listen only */
2298   }
2299
2300
2301   if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "BINDTO"))
2302   {
2303     GNUNET_break (GNUNET_OK ==
2304                   GNUNET_CONFIGURATION_get_value_string (cfg, serviceName,
2305                                                          "BINDTO", &hostname));
2306   }
2307   else
2308     hostname = NULL;
2309
2310   if (hostname != NULL)
2311   {
2312     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2313                      "Resolving `%s' since that is where `%s' will bind to.\n",
2314                      hostname, serviceName);
2315     memset (&hints, 0, sizeof (struct addrinfo));
2316     if (disablev6)
2317       hints.ai_family = AF_INET;
2318     if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) ||
2319         (res == NULL))
2320     {
2321       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to resolve `%s': %s\n"),
2322                   hostname, gai_strerror (ret));
2323       GNUNET_free (hostname);
2324       return GNUNET_SYSERR;
2325     }
2326     next = res;
2327     i = 0;
2328     while (NULL != (pos = next))
2329     {
2330       next = pos->ai_next;
2331       if ((disablev6) && (pos->ai_family == AF_INET6))
2332         continue;
2333       i++;
2334     }
2335     if (0 == i)
2336     {
2337       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2338                   _("Failed to find %saddress for `%s'.\n"),
2339                   disablev6 ? "IPv4 " : "", hostname);
2340       freeaddrinfo (res);
2341       GNUNET_free (hostname);
2342       return GNUNET_SYSERR;
2343     }
2344     resi = i;
2345     saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
2346     saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
2347     i = 0;
2348     next = res;
2349     while (NULL != (pos = next))
2350     {
2351       next = pos->ai_next;
2352       if ((disablev6) && (pos->ai_family == AF_INET6))
2353         continue;
2354       if ((pos->ai_protocol != IPPROTO_TCP) && (pos->ai_protocol != 0))
2355         continue;               /* not TCP */
2356       if ((pos->ai_socktype != SOCK_STREAM) && (pos->ai_socktype != 0))
2357         continue;               /* huh? */
2358       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2359                        "Service will bind to `%s'\n", GNUNET_a2s (pos->ai_addr,
2360                                                                   pos->ai_addrlen));
2361       if (pos->ai_family == AF_INET)
2362       {
2363         GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in));
2364         saddrlens[i] = pos->ai_addrlen;
2365         saddrs[i] = GNUNET_malloc (saddrlens[i]);
2366         memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
2367         ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
2368       }
2369       else
2370       {
2371         GNUNET_assert (pos->ai_family == AF_INET6);
2372         GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6));
2373         saddrlens[i] = pos->ai_addrlen;
2374         saddrs[i] = GNUNET_malloc (saddrlens[i]);
2375         memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
2376         ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
2377       }
2378       i++;
2379     }
2380     GNUNET_free (hostname);
2381     freeaddrinfo (res);
2382     resi = i;
2383   }
2384   else
2385   {
2386     /* will bind against everything, just set port */
2387     if (disablev6)
2388     {
2389       /* V4-only */
2390       resi = 1;
2391       i = 0;
2392       saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
2393       saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
2394
2395       saddrlens[i] = sizeof (struct sockaddr_in);
2396       saddrs[i] = GNUNET_malloc (saddrlens[i]);
2397 #if HAVE_SOCKADDR_IN_SIN_LEN
2398       ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i];
2399 #endif
2400       ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
2401       ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
2402     }
2403     else
2404     {
2405       /* dual stack */
2406       resi = 2;
2407       saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
2408       saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
2409       i = 0;
2410       saddrlens[i] = sizeof (struct sockaddr_in6);
2411       saddrs[i] = GNUNET_malloc (saddrlens[i]);
2412 #if HAVE_SOCKADDR_IN_SIN_LEN
2413       ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0];
2414 #endif
2415       ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6;
2416       ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
2417       i++;
2418       saddrlens[i] = sizeof (struct sockaddr_in);
2419       saddrs[i] = GNUNET_malloc (saddrlens[i]);
2420 #if HAVE_SOCKADDR_IN_SIN_LEN
2421       ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1];
2422 #endif
2423       ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
2424       ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
2425     }
2426   }
2427   *addrs = saddrs;
2428   *addr_lens = saddrlens;
2429   return resi;
2430 }
2431
2432
2433 /**
2434  * Ask NAT for addresses
2435  *
2436  * @param plugin the plugin handle
2437  */
2438 static void
2439 server_start_report_addresses (struct HTTP_Server_Plugin *plugin)
2440 {
2441   int res = GNUNET_OK;
2442   struct sockaddr **addrs;
2443   socklen_t *addrlens;
2444
2445   res = server_get_addresses (plugin,
2446                               plugin->name, plugin->env->cfg,
2447                               &addrs, &addrlens);
2448   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2449                    _("Found %u addresses to report to NAT service\n"), res);
2450
2451   if (GNUNET_SYSERR == res)
2452   {
2453     plugin->nat = NULL;
2454     return;
2455   }
2456
2457   plugin->nat =
2458       GNUNET_NAT_register (plugin->env->cfg, GNUNET_YES, plugin->port,
2459                            (unsigned int) res,
2460                            (const struct sockaddr **) addrs, addrlens,
2461                            &server_nat_port_map_callback, NULL, plugin);
2462   while (res > 0)
2463   {
2464     res--;
2465     GNUNET_assert (addrs[res] != NULL);
2466     GNUNET_free (addrs[res]);
2467   }
2468   GNUNET_free_non_null (addrs);
2469   GNUNET_free_non_null (addrlens);
2470 }
2471
2472
2473 /**
2474  * Stop NAT for addresses
2475  *
2476  * @param plugin the plugin handle
2477  */
2478 static void
2479 server_stop_report_addresses (struct HTTP_Server_Plugin *plugin)
2480 {
2481   /* Stop NAT handle */
2482   if (NULL != plugin->nat)
2483     GNUNET_NAT_unregister (plugin->nat);
2484
2485   /* Clean up addresses */
2486   struct HttpAddressWrapper *w;
2487
2488   while (plugin->addr_head != NULL)
2489   {
2490     w = plugin->addr_head;
2491     GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w);
2492     GNUNET_free (w->addr);
2493     GNUNET_free (w);
2494   }
2495 }
2496
2497
2498 /**
2499  * Check if IPv6 supported on this system
2500  *
2501  * @param plugin the plugin handle
2502  * @return GNUNET_YES on success, else GNUNET_NO
2503  */
2504 static int
2505 server_check_ipv6_support (struct HTTP_Server_Plugin *plugin)
2506 {
2507   struct GNUNET_NETWORK_Handle *desc = NULL;
2508   int res = GNUNET_NO;
2509
2510   /* Probe IPv6 support */
2511   desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
2512   if (NULL == desc)
2513   {
2514     if ((errno == ENOBUFS) || (errno == ENOMEM) || (errno == ENFILE) ||
2515         (errno == EACCES))
2516     {
2517       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
2518     }
2519     GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
2520                      _
2521                      ("Disabling IPv6 since it is not supported on this system!\n"));
2522     res = GNUNET_NO;
2523   }
2524   else
2525   {
2526     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
2527     desc = NULL;
2528     res = GNUNET_YES;
2529   }
2530   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2531                    "Testing IPv6 on this system: %s\n",
2532                    (res == GNUNET_YES) ? "successful" : "failed");
2533   return res;
2534 }
2535
2536
2537 /**
2538  * Function called when the service shuts down.  Unloads our plugins
2539  * and cancels pending validations.
2540  *
2541  * @param cls closure, unused
2542  * @param tc task context (unused)
2543  */
2544 static void
2545 server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2546 {
2547   struct HTTP_Server_Plugin *plugin = cls;
2548
2549   plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK;
2550
2551   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2552     return;
2553
2554   GNUNET_asprintf(&plugin->ext_addr, "%s://%s", plugin->protocol, plugin->external_hostname);
2555   plugin->ext_addr_len = strlen (plugin->ext_addr) + 1;
2556   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2557                    "Notifying transport about external hostname address `%s'\n", plugin->ext_addr);
2558
2559 #if BUILD_HTTPS
2560   plugin->env->notify_address (plugin->env->cls, GNUNET_YES,
2561                                plugin->ext_addr, plugin->ext_addr_len,
2562                                "https_client");
2563 #else
2564   plugin->env->notify_address (plugin->env->cls, GNUNET_YES,
2565                                plugin->ext_addr, plugin->ext_addr_len,
2566                                "http_client");
2567 #endif
2568 }
2569
2570
2571 /**
2572  * Configure the plugin
2573  *
2574  * @param plugin plugin handle
2575  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
2576  */
2577 static int
2578 server_configure_plugin (struct HTTP_Server_Plugin *plugin)
2579 {
2580   unsigned long long port;
2581   unsigned long long max_connections;
2582   char *bind4_address = NULL;
2583   char *bind6_address = NULL;
2584
2585   /* Use IPv4? */
2586   if (GNUNET_CONFIGURATION_have_value
2587       (plugin->env->cfg, plugin->name, "USE_IPv4"))
2588   {
2589     plugin->use_ipv4 =
2590         GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name,
2591                                               "USE_IPv4");
2592   }
2593   else
2594     plugin->use_ipv4 = GNUNET_YES;
2595   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2596                    _("IPv4 support is %s\n"),
2597                    (plugin->use_ipv4 == GNUNET_YES) ? "enabled" : "disabled");
2598
2599   /* Use IPv6? */
2600   if (GNUNET_CONFIGURATION_have_value
2601       (plugin->env->cfg, plugin->name, "USE_IPv6"))
2602   {
2603     plugin->use_ipv6 =
2604         GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name,
2605                                               "USE_IPv6");
2606   }
2607   else
2608     plugin->use_ipv6 = GNUNET_YES;
2609   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2610                    _("IPv6 support is %s\n"),
2611                    (plugin->use_ipv6 == GNUNET_YES) ? "enabled" : "disabled");
2612
2613   if ((plugin->use_ipv4 == GNUNET_NO) && (plugin->use_ipv6 == GNUNET_NO))
2614   {
2615     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2616                      _
2617                      ("Neither IPv4 nor IPv6 are enabled! Fix in configuration\n"),
2618                      plugin->name);
2619     return GNUNET_SYSERR;
2620   }
2621
2622   /* Reading port number from config file */
2623   if ((GNUNET_OK !=
2624        GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, plugin->name,
2625                                               "PORT", &port)) || (port > 65535))
2626   {
2627     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2628                      _("Port is required! Fix in configuration\n"),
2629                      plugin->name);
2630     return GNUNET_SYSERR;
2631   }
2632   plugin->port = port;
2633
2634   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2635                    _("Using port %u\n"), plugin->port);
2636
2637   if ((plugin->use_ipv4 == GNUNET_YES) &&
2638       (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2639                           plugin->name, "BINDTO", &bind4_address)))
2640   {
2641     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2642                      "Binding %s plugin to specific IPv4 address: `%s'\n",
2643                      plugin->protocol, bind4_address);
2644     plugin->server_addr_v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
2645     if (1 != inet_pton (AF_INET, bind4_address,
2646                         &plugin->server_addr_v4->sin_addr))
2647     {
2648         GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2649                          _
2650                          ("Specific IPv4 address `%s' in configuration file is invalid!\n"),
2651                          bind4_address);
2652       GNUNET_free (bind4_address);
2653       GNUNET_free (plugin->server_addr_v4);
2654       plugin->server_addr_v4 = NULL;
2655       return GNUNET_SYSERR;
2656     }
2657     else
2658     {
2659       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2660                          _("Binding to IPv4 address %s\n"), bind4_address);
2661       plugin->server_addr_v4->sin_family = AF_INET;
2662       plugin->server_addr_v4->sin_port = htons (plugin->port);
2663     }
2664     GNUNET_free (bind4_address);
2665   }
2666
2667   if ((plugin->use_ipv6 == GNUNET_YES) &&
2668       (GNUNET_YES ==
2669        GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
2670                                               "BINDTO6", &bind6_address)))
2671   {
2672     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2673                      "Binding %s plugin to specific IPv6 address: `%s'\n",
2674                      plugin->protocol, bind6_address);
2675     plugin->server_addr_v6 = GNUNET_malloc (sizeof (struct sockaddr_in6));
2676     if (1 !=
2677         inet_pton (AF_INET6, bind6_address, &plugin->server_addr_v6->sin6_addr))
2678     {
2679       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2680                        _
2681                        ("Specific IPv6 address `%s' in configuration file is invalid!\n"),
2682                        bind6_address);
2683       GNUNET_free (bind6_address);
2684       GNUNET_free (plugin->server_addr_v6);
2685       plugin->server_addr_v6 = NULL;
2686       return GNUNET_SYSERR;
2687     }
2688     else
2689     {
2690       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2691                          _("Binding to IPv6 address %s\n"), bind6_address);
2692       plugin->server_addr_v6->sin6_family = AF_INET6;
2693       plugin->server_addr_v6->sin6_port = htons (plugin->port);
2694     }
2695     GNUNET_free (bind6_address);
2696   }
2697
2698   if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
2699                                               "EXTERNAL_HOSTNAME", &plugin->external_hostname))
2700   {
2701       char * tmp = NULL;
2702       if (NULL != strstr(plugin->external_hostname, "://"))
2703       {
2704           tmp = strdup(&strstr(plugin->external_hostname, "://")[3]);
2705           GNUNET_free (plugin->external_hostname);
2706           plugin->external_hostname = tmp;
2707
2708       }
2709       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2710                        _("Using external hostname `%s'\n"), plugin->external_hostname);
2711       plugin->notify_ext_task = GNUNET_SCHEDULER_add_now (&server_notify_external_hostname, plugin);
2712
2713       /* Use only configured external hostname */
2714       if (GNUNET_CONFIGURATION_have_value
2715           (plugin->env->cfg, plugin->name, "EXTERNAL_HOSTNAME_ONLY"))
2716       {
2717         plugin->external_only =
2718             GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name,
2719                                                   "EXTERNAL_HOSTNAME_ONLY");
2720       }
2721       else
2722         plugin->external_only = GNUNET_NO;
2723
2724       if (GNUNET_YES == plugin->external_only)
2725         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2726                          _("Notifying transport only about hostname `%s'\n"), plugin->external_hostname);
2727   }
2728   else
2729     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2730                      "No external hostname configured\n");
2731
2732   /* Optional parameters */
2733   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
2734                       plugin->name,
2735                       "MAX_CONNECTIONS", &max_connections))
2736     max_connections = 128;
2737   plugin->max_connections = max_connections;
2738
2739   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2740                    _("Maximum number of connections is %u\n"),
2741                    plugin->max_connections);
2742
2743
2744   plugin->peer_id_length = strlen (GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey));
2745
2746   return GNUNET_OK;
2747 }
2748
2749
2750 /**
2751  * Session was idle, so disconnect it
2752  *
2753  * @param cls the session
2754  * @param tc task context
2755  */
2756 static void
2757 server_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2758 {
2759   GNUNET_assert (NULL != cls);
2760   struct Session *s = cls;
2761
2762   s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
2763   GNUNET_log (TIMEOUT_LOG,
2764               "Session %p was idle for %llu ms, disconnecting\n",
2765               s, (unsigned long long) SERVER_SESSION_TIMEOUT.rel_value);
2766
2767   /* call session destroy function */
2768  GNUNET_assert (GNUNET_OK == server_disconnect (s));
2769 }
2770
2771
2772 /**
2773 * Start session timeout for session s
2774 *
2775 * @param s the session
2776 */
2777 static void
2778 server_start_session_timeout (struct Session *s)
2779 {
2780  GNUNET_assert (NULL != s);
2781  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s->timeout_task);
2782  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (SERVER_SESSION_TIMEOUT,
2783                                                   &server_session_timeout,
2784                                                   s);
2785  GNUNET_log (TIMEOUT_LOG,
2786              "Timeout for session %p set to %llu ms\n",
2787              s,  (unsigned long long) SERVER_SESSION_TIMEOUT.rel_value);
2788 }
2789
2790
2791 /**
2792 * Increment session timeout due to activity session s
2793 *
2794 * @param s the session
2795 */
2796 static void
2797 server_reschedule_session_timeout (struct Session *s)
2798 {
2799  GNUNET_assert (NULL != s);
2800  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task);
2801
2802  GNUNET_SCHEDULER_cancel (s->timeout_task);
2803  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (SERVER_SESSION_TIMEOUT,
2804                                                   &server_session_timeout,
2805                                                   s);
2806  GNUNET_log (TIMEOUT_LOG,
2807              "Timeout rescheduled for session %p set to %llu ms\n",
2808              s, (unsigned long long) SERVER_SESSION_TIMEOUT.rel_value);
2809 }
2810
2811
2812 /**
2813  * Exit point from the plugin.
2814  *
2815  * @param cls api
2816  * @return NULL
2817  */
2818 void *
2819 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
2820 {
2821   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2822   struct HTTP_Server_Plugin *plugin = api->cls;
2823   struct Session *pos;
2824   struct Session *next;
2825
2826   if (NULL == api->cls)
2827   {
2828     /* Free for stub mode */
2829     GNUNET_free (api);
2830     return NULL;
2831   }
2832   plugin->in_shutdown = GNUNET_YES;
2833   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2834                    _("Shutting down plugin `%s'\n"),
2835                    plugin->name);
2836
2837   if (GNUNET_SCHEDULER_NO_TASK != plugin->notify_ext_task)
2838   {
2839       GNUNET_SCHEDULER_cancel (plugin->notify_ext_task);
2840       plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK;
2841   }
2842
2843   if (NULL != plugin->ext_addr)
2844   {
2845       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2846                        "Notifying transport to remove address `%s'\n",
2847                        http_common_plugin_address_to_string (NULL,
2848                            plugin->ext_addr,
2849                            plugin->ext_addr_len));
2850 #if BUILD_HTTPS
2851       plugin->env->notify_address (plugin->env->cls,
2852                                    GNUNET_NO,
2853                                    plugin->ext_addr,
2854                                    plugin->ext_addr_len,
2855                                    "https_client");
2856 #else
2857   plugin->env->notify_address (plugin->env->cls,
2858                                GNUNET_NO,
2859                                plugin->ext_addr,
2860                                plugin->ext_addr_len,
2861                                "http_client");
2862 #endif
2863
2864   }
2865
2866   /* Stop to report addresses to transport service */
2867   server_stop_report_addresses (plugin);
2868   server_stop (plugin);
2869   next = plugin->head;
2870   while (NULL != (pos = next))
2871   {
2872       next = pos->next;
2873       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2874                        "Removing left over session %p\n", pos);
2875
2876       if ((GNUNET_YES == pos->session_passed) && (GNUNET_NO == pos->session_ended))
2877       {
2878         /* Notify transport immediately that this session is invalid */
2879         pos->session_ended = GNUNET_YES;
2880         plugin->env->session_end (plugin->env->cls, &pos->target, pos);
2881       }
2882
2883       server_delete_session (pos);
2884   }
2885
2886   /* Clean up */
2887   GNUNET_free_non_null (plugin->external_hostname);
2888   GNUNET_free_non_null (plugin->ext_addr);
2889   GNUNET_free_non_null (plugin->server_addr_v4);
2890   GNUNET_free_non_null (plugin->server_addr_v6);
2891
2892   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2893                    _("Shutdown for plugin `%s' complete\n"),
2894                    plugin->name);
2895
2896   GNUNET_free (plugin);
2897   GNUNET_free (api);
2898   return NULL;
2899 }
2900
2901
2902 /**
2903  * Entry point for the plugin.
2904  *
2905  * @param cls env
2906  * @return api
2907  */
2908 void *
2909 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
2910 {
2911   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2912   struct GNUNET_TRANSPORT_PluginFunctions *api;
2913   struct HTTP_Server_Plugin *plugin;
2914
2915   plugin = GNUNET_malloc (sizeof (struct HTTP_Server_Plugin));
2916   plugin->env = env;
2917   p = plugin;
2918
2919   if (NULL == env->receive)
2920   {
2921     /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
2922        initialze the plugin or the API */
2923     api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2924     api->cls = NULL;
2925     api->address_to_string = &http_common_plugin_address_to_string;
2926     api->string_to_address = &http_common_plugin_string_to_address;
2927     api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2928     return api;
2929   }
2930
2931   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2932   api->cls = plugin;
2933   api->send = &http_server_plugin_send;
2934   api->disconnect = &http_server_plugin_disconnect;
2935   api->check_address = &http_server_plugin_address_suggested;
2936   api->get_session = &http_server_plugin_get_session;
2937
2938   api->address_to_string = &http_common_plugin_address_to_string;
2939   api->string_to_address = &http_common_plugin_string_to_address;
2940   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2941
2942 #if BUILD_HTTPS
2943   plugin->name = "transport-https_server";
2944   plugin->protocol = "https";
2945 #else
2946   plugin->name = "transport-http_server";
2947   plugin->protocol = "http";
2948 #endif
2949
2950   /* Configure plugin */
2951   if (GNUNET_SYSERR == server_configure_plugin (plugin))
2952   {
2953       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2954       return NULL;
2955   }
2956
2957   /* Check IPv6 support */
2958   if (GNUNET_YES == plugin->use_ipv6)
2959     plugin->use_ipv6 = server_check_ipv6_support (plugin);
2960
2961   /* Report addresses to transport service */
2962   if (GNUNET_NO == plugin->external_only)
2963     server_start_report_addresses (plugin);
2964
2965   if (GNUNET_SYSERR == server_start (plugin))
2966   {
2967       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2968       return NULL;
2969   }
2970
2971   return api;
2972 }
2973
2974 /* end of plugin_transport_http_server.c */