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