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