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