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