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