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