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