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