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