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