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