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