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