(no commit message)
[oweals/gnunet.git] / src / transport / plugin_transport_https.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_https.c
23  * @brief https transport service plugin
24  * @author Matthias Wachs
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_connection_lib.h"
32 #include "gnunet_service_lib.h"
33 #include "gnunet_statistics_service.h"
34 #include "gnunet_transport_service.h"
35 #include "gnunet_resolver_service.h"
36 #include "gnunet_server_lib.h"
37 #include "gnunet_container_lib.h"
38 #include "plugin_transport.h"
39 #include "gnunet_os_lib.h"
40 #include "gnunet_disk_lib.h"
41 #include "microhttpd.h"
42 #include <curl/curl.h>
43
44 #define DEBUG_HTTPS GNUNET_NO
45 #define DEBUG_CURL GNUNET_NO
46 #define DEBUG_CONNECTIONS GNUNET_NO
47 #define DEBUG_SESSION_SELECTION GNUNET_NO
48
49 #define INBOUND GNUNET_NO
50 #define OUTBOUND GNUNET_YES
51
52 #define PROTOCOL_PREFIX "https"
53
54 /**
55  * Text of the response sent back after the last bytes of a PUT
56  * request have been received (just to formally obey the HTTP
57  * protocol).
58  */
59 #define HTTP_PUT_RESPONSE "Thank you!"
60
61 /**
62  * After how long do we expire an address that we
63  * learned from another peer if it is not reconfirmed
64  * by anyone?
65  */
66 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
67
68 /**
69  * Page returned if request invalid
70  */
71 #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>"
72
73 /**
74  * Timeout for a http connect
75  */
76 #define HTTP_CONNECT_TIMEOUT 30
77
78 /**
79  * Network format for IPv4 addresses.
80  */
81 struct IPv4HttpAddress
82 {
83   /**
84    * IPv4 address, in network byte order.
85    */
86   uint32_t ipv4_addr GNUNET_PACKED;
87
88   /**
89    * Port number, in network byte order.
90    */
91   uint16_t u_port GNUNET_PACKED;
92
93 };
94
95
96 /**
97  * Network format for IPv6 addresses.
98  */
99 struct IPv6HttpAddress
100 {
101   /**
102    * IPv6 address.
103    */
104   struct in6_addr ipv6_addr GNUNET_PACKED;
105
106   /**
107    * Port number, in network byte order.
108    */
109   uint16_t u6_port GNUNET_PACKED;
110
111 };
112
113
114 /**
115  *  Message to send using http
116  */
117 struct HTTP_Message
118 {
119   /**
120    * next pointer for double linked list
121    */
122   struct HTTP_Message * next;
123
124   /**
125    * previous pointer for double linked list
126    */
127   struct HTTP_Message * prev;
128
129   /**
130    * buffer containing data to send
131    */
132   char *buf;
133
134   /**
135    * amount of data already sent
136    */
137   size_t pos;
138
139   /**
140    * buffer length
141    */
142   size_t size;
143
144   /**
145    * Continuation function to call once the transmission buffer
146    * has again space available.  NULL if there is no
147    * continuation to call.
148    */
149   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
150
151   /**
152    * Closure for transmit_cont.
153    */
154   void *transmit_cont_cls;
155 };
156
157
158 struct HTTP_PeerContext
159 {
160   /**
161    * peer's identity
162    */
163   struct GNUNET_PeerIdentity identity;
164
165   /**
166    * Pointer to the global plugin struct.
167    */
168   struct Plugin *plugin;
169
170   /**
171    * Linked list of connections with this peer
172    * head
173    */
174   struct Session * head;
175
176   /**
177    * Linked list of connections with this peer
178    * tail
179    */
180   struct Session * tail;
181
182   /**
183    * id for next session
184    */
185   size_t session_id_counter;
186
187   /**
188    * Last session used to send data
189    */
190   struct Session * last_session;
191 };
192
193
194 struct Session
195 {
196   /**
197    * API requirement.
198    */
199   struct SessionHeader header;
200
201   /**
202    * next session in linked list
203    */
204   struct Session * next;
205
206   /**
207    * previous session in linked list
208    */
209   struct Session * prev;
210
211   /**
212    * address of this session
213    */
214   void * addr;
215
216   /**
217    * address length
218    */
219   size_t addrlen;
220
221   /**
222    * target url
223    */
224   char * url;
225
226   /**
227    * Message queue for outbound messages
228    * head of queue
229    */
230   struct HTTP_Message * pending_msgs_head;
231
232   /**
233    * Message queue for outbound messages
234    * tail of queue
235    */
236   struct HTTP_Message * pending_msgs_tail;
237
238   /**
239    * partner peer this connection belongs to
240    */
241   struct HTTP_PeerContext * peercontext;
242
243   /**
244    * message stream tokenizer for incoming data
245    */
246   struct GNUNET_SERVER_MessageStreamTokenizer *msgtok;
247
248   /**
249    * session direction
250    * outbound: OUTBOUND (GNUNET_YES)
251    * inbound : INBOUND (GNUNET_NO)
252    */
253   unsigned int direction;
254
255   /**
256    * is session connected to send data?
257    */
258   unsigned int send_connected;
259
260   /**
261    * is send connection active?
262    */
263   unsigned int send_active;
264
265   /**
266    * connection disconnect forced (e.g. from transport)
267    */
268   unsigned int send_force_disconnect;
269
270   /**
271    * is session connected to receive data?
272    */
273   unsigned int recv_connected;
274
275   /**
276    * is receive connection active?
277    */
278   unsigned int recv_active;
279
280   /**
281    * connection disconnect forced (e.g. from transport)
282    */
283   unsigned int recv_force_disconnect;
284
285   /**
286    * id for next session
287    * NOTE: 0 is not an ID, zero is not defined. A correct ID is always > 0
288    */
289   size_t session_id;
290
291   /**
292    * entity managing sending data
293    * outbound session: CURL *
294    * inbound session: mhd_connection *
295    */
296   void * send_endpoint;
297
298   /**
299    * entity managing recieving data
300    * outbound session: CURL *
301    * inbound session: mhd_connection *
302    */
303   void * recv_endpoint;
304 };
305
306 /**
307  * Encapsulation of all of the state of the plugin.
308  */
309 struct Plugin
310 {
311   /**
312    * Our environment.
313    */
314   struct GNUNET_TRANSPORT_PluginEnvironment *env;
315
316   /**
317    * Handle for reporting statistics.
318    */
319   struct GNUNET_STATISTICS_Handle *stats;
320
321   unsigned int port_inbound;
322
323   struct GNUNET_CONTAINER_MultiHashMap *peers;
324
325   /**
326    * Daemon for listening for new IPv4 connections.
327    */
328   struct MHD_Daemon *http_server_daemon_v4;
329
330   /**
331    * Daemon for listening for new IPv6connections.
332    */
333   struct MHD_Daemon *http_server_daemon_v6;
334
335   /**
336    * Our primary task for http daemon handling IPv4 connections
337    */
338   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v4;
339
340   /**
341    * Our primary task for http daemon handling IPv6 connections
342    */
343   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v6;
344
345   /**
346    * The task sending data
347    */
348   GNUNET_SCHEDULER_TaskIdentifier http_curl_task;
349
350   /**
351    * cURL Multihandle
352    */
353   CURLM * multi_handle;
354
355   /**
356    * Our ASCII encoded, hashed peer identity
357    * This string is used to distinguish between connections and is added to the urls
358    */
359   struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
360
361   struct sockaddr_in * bind4_address;
362   struct sockaddr_in6 * bind6_address;
363   char * bind_hostname;
364   int use_ipv6;
365   int use_ipv4;
366
367   /* The certificate MHD uses as an \0 terminated string */
368   char * cert;
369
370   /* The private key MHD uses as an \0 terminated string */
371   char * key;
372 };
373
374
375 /**
376  * Function called for a quick conversion of the binary address to
377  * a numeric address.  Note that the caller must not free the
378  * address and that the next call to this function is allowed
379  * to override the address again.
380  *
381  * @param cls closure
382  * @param addr binary address
383  * @param addrlen length of the address
384  * @return string representing the same address
385  */
386 static const char*
387 http_plugin_address_to_string (void *cls,
388                                    const void *addr,
389                                    size_t addrlen);
390
391
392 /**
393  * Call MHD to process pending ipv4 requests and then go back
394  * and schedule the next run.
395  */
396 static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
397 /**
398  * Call MHD to process pending ipv6 requests and then go back
399  * and schedule the next run.
400  */
401 static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
402
403 /**
404  * Function setting up curl handle and selecting message to send
405  * @param cls plugin
406  * @param ses session to send data to
407  * @param con connection
408  * @return bytes sent to peer
409  */
410 static ssize_t send_check_connections (void *cls, struct Session *ps);
411
412 /**
413  * Function setting up file descriptors and scheduling task to run
414  * @param cls closure
415  * @param ses session to send data to
416  * @param
417  */
418 static int curl_schedule(void *cls );
419
420
421
422 static char * create_url(void * cls, const void * addr, size_t addrlen, size_t id)
423 {
424   struct Plugin *plugin = cls;
425   char *url = NULL;
426
427   GNUNET_assert ((addr!=NULL) && (addrlen != 0));
428   GNUNET_asprintf(&url,
429                   "%s://%s/%s;%u", PROTOCOL_PREFIX,
430                   http_plugin_address_to_string(NULL, addr, addrlen),
431                   (char *) (&plugin->my_ascii_hash_ident),id);
432
433   return url;
434 }
435
436 /**
437  * Removes a message from the linked list of messages
438  * @param con connection to remove message from
439  * @param msg message to remove
440  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
441  */
442 static int remove_http_message (struct Session * ps, struct HTTP_Message * msg)
443 {
444   GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_tail,msg);
445   GNUNET_free(msg);
446   return GNUNET_OK;
447 }
448
449 int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value);
450
451 /**
452  * Removes a session from the linked list of sessions
453  * @param pc peer context
454  * @param ps session
455  * @param call_msg_cont GNUNET_YES to call pending message continuations, otherwise no
456  * @param call_msg_cont_result, result to call message continuations with
457  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
458  */
459 static int remove_session (struct HTTP_PeerContext * pc, struct Session * ps,  int call_msg_cont, int call_msg_cont_result)
460 {
461   struct HTTP_Message * msg;
462   struct Plugin * plugin = ps->peercontext->plugin;
463
464 #if DEBUG_CONNECTIONS
465   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: removing %s session %X with id %u\n", ps, (ps->direction == INBOUND) ? "inbound" : "outbound", ps, ps->session_id);
466 #endif
467   plugin->env->session_end(plugin, &pc->identity, ps);
468
469   GNUNET_free_non_null (ps->addr);
470   GNUNET_SERVER_mst_destroy (ps->msgtok);
471   GNUNET_free(ps->url);
472
473   if (ps->direction==INBOUND)
474   {
475           if (ps->recv_endpoint != NULL)
476           {
477                   curl_easy_cleanup(ps->recv_endpoint);
478                   ps->recv_endpoint = NULL;
479           }
480           if (ps->send_endpoint != NULL)
481           {
482                   curl_easy_cleanup(ps->send_endpoint);
483                   ps->send_endpoint = NULL;
484           }
485   }
486
487   msg = ps->pending_msgs_head;
488   while (msg!=NULL)
489   {
490     if ((call_msg_cont == GNUNET_YES) && (msg->transmit_cont!=NULL))
491     {
492       msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,call_msg_cont_result);
493     }
494     GNUNET_free(msg);
495     GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_head,msg);
496     msg = ps->pending_msgs_head;
497   }
498
499   GNUNET_CONTAINER_DLL_remove(pc->head,pc->tail,ps);
500   GNUNET_free(ps);
501   ps = NULL;
502
503   /* no sessions left remove peer */
504   if (pc->head==NULL)
505   {
506 #if DEBUG_HTTP
507   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No sessions left for peer `%s', removing context\n",GNUNET_i2s(&pc->identity));
508 #endif
509         remove_peer_context_Iterator(plugin, &pc->identity.hashPubKey, pc);
510   }
511
512   return GNUNET_OK;
513 }
514
515 int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value)
516 {
517   struct Plugin *plugin = cls;
518   struct HTTP_PeerContext * pc = value;
519   struct Session * ps = pc->head;
520   struct Session * tmp = NULL;
521   struct HTTP_Message * msg = NULL;
522   struct HTTP_Message * msg_tmp = NULL;
523 #if DEBUG_HTTP
524   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing context for peer `%s'\n",GNUNET_i2s(&pc->identity));
525 #endif
526   GNUNET_CONTAINER_multihashmap_remove (plugin->peers, &pc->identity.hashPubKey, pc);
527   while (ps!=NULL)
528   {
529         plugin->env->session_end(plugin, &pc->identity, ps);
530         tmp = ps->next;
531
532     GNUNET_free_non_null (ps->addr);
533     GNUNET_free(ps->url);
534     if (ps->msgtok != NULL)
535       GNUNET_SERVER_mst_destroy (ps->msgtok);
536
537     msg = ps->pending_msgs_head;
538     while (msg!=NULL)
539     {
540       msg_tmp = msg->next;
541       GNUNET_free(msg);
542       msg = msg_tmp;
543     }
544     if (ps->direction==OUTBOUND)
545     {
546       if (ps->send_endpoint!=NULL)
547         curl_easy_cleanup(ps->send_endpoint);
548       if (ps->recv_endpoint!=NULL)
549         curl_easy_cleanup(ps->recv_endpoint);
550     }
551
552     GNUNET_free(ps);
553     ps=tmp;
554   }
555   GNUNET_free(pc);
556   GNUNET_STATISTICS_update (plugin->env->stats,
557                             gettext_noop ("# HTTP peers active"),
558                             -1,
559                             GNUNET_NO);
560   return GNUNET_YES;
561 }
562
563 /**
564  * Add the IP of our network interface to the list of
565  * our external IP addresses.
566  *
567  * @param cls the 'struct Plugin*'
568  * @param name name of the interface
569  * @param isDefault do we think this may be our default interface
570  * @param addr address of the interface
571  * @param addrlen number of bytes in addr
572  * @return GNUNET_OK to continue iterating
573  */
574 static int
575 process_interfaces (void *cls,
576                     const char *name,
577                     int isDefault,
578                     const struct sockaddr *addr, socklen_t addrlen)
579 {
580   struct Plugin *plugin = cls;
581   struct IPv4HttpAddress * t4;
582   struct IPv6HttpAddress * t6;
583   int af;
584
585
586   GNUNET_assert(cls !=NULL);
587   af = addr->sa_family;
588   if ((af == AF_INET) && (plugin->use_ipv4 == GNUNET_YES) && (plugin->bind6_address == NULL))
589     {
590           struct in_addr bnd_cmp = ((struct sockaddr_in *) addr)->sin_addr;
591       t4 = GNUNET_malloc(sizeof(struct IPv4HttpAddress));
592       /* Not skipping loopback addresses
593       if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr))
594       {
595
596         return GNUNET_OK;
597       }
598       */
599       t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
600       t4->u_port = htons (plugin->port_inbound);
601       if (plugin->bind4_address != NULL)
602       {
603           if (0 == memcmp(&plugin->bind4_address->sin_addr, &bnd_cmp, sizeof (struct in_addr)))
604           {
605                   plugin->env->notify_address(plugin->env->cls,"https",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL);
606           }
607       }
608       else
609       {
610           plugin->env->notify_address(plugin->env->cls,"https",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL);
611       }
612       GNUNET_free (t4);
613     }
614   else if ((af == AF_INET6) && (plugin->use_ipv6 == GNUNET_YES)  && (plugin->bind4_address == NULL))
615     {
616           struct in6_addr bnd_cmp6 = ((struct sockaddr_in6 *) addr)->sin6_addr;
617       t6 = GNUNET_malloc(sizeof(struct IPv6HttpAddress));
618       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
619         {
620           return GNUNET_OK;
621         }
622
623       if (plugin->bind6_address != NULL)
624       {
625           if (0 == memcmp(&plugin->bind6_address->sin6_addr, &bnd_cmp6, sizeof (struct in6_addr)))
626           {
627               memcpy (&t6->ipv6_addr,
628                       &((struct sockaddr_in6 *) addr)->sin6_addr,
629                       sizeof (struct in6_addr));
630               t6->u6_port = htons (plugin->port_inbound);
631               plugin->env->notify_address(plugin->env->cls,"https",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL);
632           }
633       }
634       else
635       {
636           memcpy (&t6->ipv6_addr,
637                   &((struct sockaddr_in6 *) addr)->sin6_addr,
638                   sizeof (struct in6_addr));
639           t6->u6_port = htons (plugin->port_inbound);
640           plugin->env->notify_address(plugin->env->cls,"https",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL);
641       }
642       GNUNET_free (t6);
643     }
644   return GNUNET_OK;
645 }
646
647
648 /**
649  * Callback called by MHD when a connection is terminated
650  */
651 static void mhd_termination_cb (void *cls, struct MHD_Connection * connection, void **httpSessionCache)
652 {
653   struct Session * ps = *httpSessionCache;
654   if (ps == NULL)
655     return;
656   struct HTTP_PeerContext * pc = ps->peercontext;
657
658   if (connection==ps->recv_endpoint)
659   {
660 #if DEBUG_CONNECTIONS
661     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity));
662 #endif
663     ps->recv_active = GNUNET_NO;
664     ps->recv_connected = GNUNET_NO;
665     ps->recv_endpoint = NULL;
666   }
667   if (connection==ps->send_endpoint)
668   {
669
670     ps->send_active = GNUNET_NO;
671     ps->send_connected = GNUNET_NO;
672     ps->send_endpoint = NULL;
673 #if DEBUG_CONNECTIONS
674     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity));
675 #endif
676   }
677
678   /* if both connections disconnected, remove session */
679   if ((ps->send_connected == GNUNET_NO) && (ps->recv_connected == GNUNET_NO))
680   {
681       GNUNET_STATISTICS_update (pc->plugin->env->stats,
682                             gettext_noop ("# HTTPS inbound sessions for peers active"),
683                             -1,
684                             GNUNET_NO);
685     remove_session(pc,ps,GNUNET_YES,GNUNET_SYSERR);
686   }
687 }
688
689 static void mhd_write_mst_cb (void *cls,
690                               void *client,
691                               const struct GNUNET_MessageHeader *message)
692 {
693
694   struct Session *ps  = cls;
695   struct HTTP_PeerContext *pc = ps->peercontext;
696   GNUNET_assert(ps != NULL);
697   GNUNET_assert(pc != NULL);
698 #if DEBUG_HTTP
699   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
701               ps,
702               ntohs(message->type),
703               ntohs(message->size),
704               GNUNET_i2s(&(ps->peercontext)->identity),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen));
705 #endif
706   pc->plugin->env->receive (ps->peercontext->plugin->env->cls,
707                             &pc->identity,
708                             message, 1, ps,
709                             NULL,
710                             0);
711 }
712
713 /**
714  * Check if ip is allowed to connect.
715  */
716 static int
717 mhd_accept_cb (void *cls,
718                       const struct sockaddr *addr, socklen_t addr_len)
719 {
720 #if 0
721   struct Plugin *plugin = cls;
722 #endif
723   /* Every connection is accepted, nothing more to do here */
724   return MHD_YES;
725 }
726
727 int mhd_send_callback (void *cls, uint64_t pos, char *buf, int max)
728 {
729   int bytes_read = 0;
730
731   struct Session * ps = cls;
732   struct HTTP_PeerContext * pc;
733   struct HTTP_Message * msg;
734
735   GNUNET_assert (ps!=NULL);
736   pc = ps->peercontext;
737   msg = ps->pending_msgs_tail;
738   if (ps->send_force_disconnect==GNUNET_YES)
739   {
740 #if DEBUG_CONNECTIONS
741     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound forced to disconnect\n",ps);
742 #endif
743     return -1;
744   }
745
746   if (msg!=NULL)
747   {
748     if ((msg->size-msg->pos) <= max)
749     {
750       memcpy(buf,&msg->buf[msg->pos],(msg->size-msg->pos));
751       bytes_read = msg->size-msg->pos;
752       msg->pos+=(msg->size-msg->pos);
753     }
754     else
755     {
756       memcpy(buf,&msg->buf[msg->pos],max);
757       msg->pos+=max;
758       bytes_read = max;
759     }
760
761     if (msg->pos==msg->size)
762     {
763       if (NULL!=msg->transmit_cont)
764         msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
765       remove_http_message(ps,msg);
766     }
767   }
768   return bytes_read;
769 }
770
771 /**
772  * Process GET or PUT request received via MHD.  For
773  * GET, queue response that will send back our pending
774  * messages.  For PUT, process incoming data and send
775  * to GNUnet core.  In either case, check if a session
776  * already exists and create a new one if not.
777  */
778 static int
779 mdh_access_cb (void *cls,
780                        struct MHD_Connection *mhd_connection,
781                        const char *url,
782                        const char *method,
783                        const char *version,
784                        const char *upload_data,
785                        size_t * upload_data_size, void **httpSessionCache)
786 {
787   struct Plugin *plugin = cls;
788   struct MHD_Response *response;
789   const union MHD_ConnectionInfo * conn_info;
790
791   struct sockaddr_in  *addrin;
792   struct sockaddr_in6 *addrin6;
793
794   char address[INET6_ADDRSTRLEN+14];
795   struct GNUNET_PeerIdentity pi_in;
796   size_t id_num = 0;
797
798   struct IPv4HttpAddress ipv4addr;
799   struct IPv6HttpAddress ipv6addr;
800
801   struct HTTP_PeerContext *pc;
802   struct Session *ps = NULL;
803   struct Session *ps_tmp = NULL;
804
805   int res = GNUNET_NO;
806   int send_error_to_client;
807   void * addr;
808   size_t addr_len;
809
810   GNUNET_assert(cls !=NULL);
811   send_error_to_client = GNUNET_NO;
812
813   if (NULL == *httpSessionCache)
814   {
815     /* check url for peer identity , if invalid send HTTP 404*/
816     size_t len = strlen(&url[1]);
817     char * peer = GNUNET_malloc(104+1);
818
819     if ((len>104) && (url[104]==';'))
820     {
821         char * id = GNUNET_malloc((len-104)+1);
822         strcpy(id,&url[105]);
823         memcpy(peer,&url[1],103);
824         peer[103] = '\0';
825         id_num = strtoul ( id, NULL , 10);
826         GNUNET_free(id);
827     }
828     res = GNUNET_CRYPTO_hash_from_string (peer, &(pi_in.hashPubKey));
829     GNUNET_free(peer);
830     if ( GNUNET_SYSERR == res )
831     {
832       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
833       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
834       MHD_destroy_response (response);
835 #if DEBUG_CONNECTIONS
836       if (res == MHD_YES)
837         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, sent HTTP 1.1/404\n");
838       else
839         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, could not send error\n");
840 #endif
841       return res;
842     }
843   }
844   else
845   {
846     ps = *httpSessionCache;
847     pc = ps->peercontext;
848   }
849
850   if (NULL == *httpSessionCache)
851   {
852     /* get peer context */
853     pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &pi_in.hashPubKey);
854     /* Peer unknown */
855     if (pc==NULL)
856     {
857       pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext));
858       pc->plugin = plugin;
859       pc->session_id_counter=1;
860       pc->last_session = NULL;
861       memcpy(&pc->identity, &pi_in, sizeof(struct GNUNET_PeerIdentity));
862       GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
863       GNUNET_STATISTICS_update (plugin->env->stats,
864                             gettext_noop ("# HTTP peers active"),
865                             1,
866                             GNUNET_NO);
867     }
868
869     conn_info = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS );
870     /* Incoming IPv4 connection */
871     if ( AF_INET == conn_info->client_addr->sin_family)
872     {
873       addrin = conn_info->client_addr;
874       inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
875       memcpy(&ipv4addr.ipv4_addr,&(addrin->sin_addr),sizeof(struct in_addr));
876       ipv4addr.u_port = addrin->sin_port;
877       addr = &ipv4addr;
878       addr_len = sizeof(struct IPv4HttpAddress);
879     }
880     /* Incoming IPv6 connection */
881     if ( AF_INET6 == conn_info->client_addr->sin_family)
882     {
883       addrin6 = (struct sockaddr_in6 *) conn_info->client_addr;
884       inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
885       memcpy(&ipv6addr.ipv6_addr,&(addrin6->sin6_addr),sizeof(struct in6_addr));
886       ipv6addr.u6_port = addrin6->sin6_port;
887       addr = &ipv6addr;
888       addr_len = sizeof(struct IPv6HttpAddress);
889     }
890
891     ps = NULL;
892     /* only inbound sessions here */
893
894     ps_tmp = pc->head;
895     while (ps_tmp!=NULL)
896     {
897       if ((ps_tmp->direction==INBOUND) && (ps_tmp->session_id == id_num) && (id_num!=0))
898       {
899         if ((ps_tmp->recv_force_disconnect!=GNUNET_YES) && (ps_tmp->send_force_disconnect!=GNUNET_YES))
900         ps=ps_tmp;
901         break;
902       }
903       ps_tmp=ps_tmp->next;
904     }
905
906     if (ps==NULL)
907     {
908       ps = GNUNET_malloc(sizeof (struct Session));
909       ps->addr = GNUNET_malloc(addr_len);
910       memcpy(ps->addr,addr,addr_len);
911       ps->addrlen = addr_len;
912       ps->direction=INBOUND;
913       ps->pending_msgs_head = NULL;
914       ps->pending_msgs_tail = NULL;
915       ps->send_connected=GNUNET_NO;
916       ps->send_active=GNUNET_NO;
917       ps->recv_connected=GNUNET_NO;
918       ps->recv_active=GNUNET_NO;
919       ps->peercontext=pc;
920       ps->session_id =id_num;
921       ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
922       GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
923       GNUNET_STATISTICS_update (plugin->env->stats,
924                             gettext_noop ("# HTTPS inbound sessions for peers active"),
925                             1,
926                             GNUNET_NO);
927     }
928
929     *httpSessionCache = ps;
930     if (ps->msgtok==NULL)
931       ps->msgtok = GNUNET_SERVER_mst_create (&mhd_write_mst_cb, ps);
932 #if DEBUG_HTTP
933     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: HTTPS Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
934                 ps,
935                 method,
936                 GNUNET_i2s(&pc->identity),
937                 http_plugin_address_to_string(NULL, ps->addr, ps->addrlen));
938 #endif
939   }
940
941   /* Is it a PUT or a GET request */
942   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
943   {
944     if (ps->recv_force_disconnect == GNUNET_YES)
945     {
946 #if DEBUG_CONNECTIONS
947       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection was forced to disconnect\n",ps);
948 #endif
949       ps->recv_active = GNUNET_NO;
950       return MHD_NO;
951     }
952     if ((*upload_data_size == 0) && (ps->recv_active==GNUNET_NO))
953     {
954       ps->recv_endpoint = mhd_connection;
955       ps->recv_connected = GNUNET_YES;
956       ps->recv_active = GNUNET_YES;
957       ps->recv_force_disconnect = GNUNET_NO;
958 #if DEBUG_CONNECTIONS
959       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound PUT connection connected\n",ps);
960 #endif
961       return MHD_YES;
962     }
963
964     /* Transmission of all data complete */
965     if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_YES))
966     {
967       response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
968       res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
969 #if DEBUG_CONNECTIONS
970       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Sent HTTP/1.1: 200 OK as PUT Response\n",ps);
971 #endif
972       MHD_destroy_response (response);
973       ps->recv_active=GNUNET_NO;
974       return MHD_YES;
975     }
976
977     /* Recieving data */
978     if ((*upload_data_size > 0) && (ps->recv_active == GNUNET_YES))
979     {
980       res = GNUNET_SERVER_mst_receive(ps->msgtok, ps, upload_data,*upload_data_size, GNUNET_NO, GNUNET_NO);
981       (*upload_data_size) = 0;
982       return MHD_YES;
983     }
984     else
985       return MHD_NO;
986   }
987   if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) )
988   {
989     if (ps->send_force_disconnect == GNUNET_YES)
990     {
991 #if DEBUG_CONNECTIONS
992       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection was  forced to disconnect\n",ps);
993 #endif
994       ps->send_active = GNUNET_NO;
995       return MHD_NO;
996     }
997           ps->send_connected = GNUNET_YES;
998           ps->send_active = GNUNET_YES;
999           ps->send_endpoint = mhd_connection;
1000           ps->send_force_disconnect = GNUNET_NO;
1001 #if DEBUG_CONNECTIONS
1002           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound GET connection connected\n",ps);
1003 #endif
1004           response = MHD_create_response_from_callback(-1,32 * 1024, &mhd_send_callback, ps, NULL);
1005           res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1006           MHD_destroy_response (response);
1007           return MHD_YES;
1008   }
1009   return MHD_NO;
1010 }
1011
1012 /**
1013  * Function that queries MHD's select sets and
1014  * starts the task waiting for them.
1015  */
1016 static GNUNET_SCHEDULER_TaskIdentifier
1017 http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle)
1018 {
1019   struct Plugin *plugin = cls;
1020   GNUNET_SCHEDULER_TaskIdentifier ret;
1021   fd_set rs;
1022   fd_set ws;
1023   fd_set es;
1024   struct GNUNET_NETWORK_FDSet *wrs;
1025   struct GNUNET_NETWORK_FDSet *wws;
1026   struct GNUNET_NETWORK_FDSet *wes;
1027   int max;
1028   unsigned long long timeout;
1029   int haveto;
1030   struct GNUNET_TIME_Relative tv;
1031
1032   GNUNET_assert(cls !=NULL);
1033   ret = GNUNET_SCHEDULER_NO_TASK;
1034   FD_ZERO(&rs);
1035   FD_ZERO(&ws);
1036   FD_ZERO(&es);
1037   wrs = GNUNET_NETWORK_fdset_create ();
1038   wes = GNUNET_NETWORK_fdset_create ();
1039   wws = GNUNET_NETWORK_fdset_create ();
1040   max = -1;
1041   GNUNET_assert (MHD_YES ==
1042                  MHD_get_fdset (daemon_handle,
1043                                 &rs,
1044                                 &ws,
1045                                 &es,
1046                                 &max));
1047   haveto = MHD_get_timeout (daemon_handle, &timeout);
1048   if (haveto == MHD_YES)
1049     tv.value = (uint64_t) timeout;
1050   else
1051     tv = GNUNET_TIME_UNIT_FOREVER_REL;
1052   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max);
1053   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max);
1054   GNUNET_NETWORK_fdset_copy_native (wes, &es, max);
1055   if (daemon_handle == plugin->http_server_daemon_v4)
1056   {
1057         if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1058         {
1059                 GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4);
1060                 plugin->http_server_daemon_v4 = GNUNET_SCHEDULER_NO_TASK;
1061         }
1062
1063     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1064                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1065                                        GNUNET_SCHEDULER_NO_TASK,
1066                                        tv,
1067                                        wrs,
1068                                        wws,
1069                                        &http_server_daemon_v4_run,
1070                                        plugin);
1071   }
1072   if (daemon_handle == plugin->http_server_daemon_v6)
1073   {
1074         if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1075         {
1076                 GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6);
1077                 plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1078         }
1079
1080     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1081                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1082                                        GNUNET_SCHEDULER_NO_TASK,
1083                                        tv,
1084                                        wrs,
1085                                        wws,
1086                                        &http_server_daemon_v6_run,
1087                                        plugin);
1088   }
1089   GNUNET_NETWORK_fdset_destroy (wrs);
1090   GNUNET_NETWORK_fdset_destroy (wws);
1091   GNUNET_NETWORK_fdset_destroy (wes);
1092   return ret;
1093 }
1094
1095 /**
1096  * Call MHD to process pending requests and then go back
1097  * and schedule the next run.
1098  */
1099 static void http_server_daemon_v4_run (void *cls,
1100                              const struct GNUNET_SCHEDULER_TaskContext *tc)
1101 {
1102   struct Plugin *plugin = cls;
1103
1104   GNUNET_assert(cls !=NULL);
1105   plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1106
1107   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1108     return;
1109
1110   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4));
1111   plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
1112   return;
1113 }
1114
1115
1116 /**
1117  * Call MHD to process pending requests and then go back
1118  * and schedule the next run.
1119  */
1120 static void http_server_daemon_v6_run (void *cls,
1121                              const struct GNUNET_SCHEDULER_TaskContext *tc)
1122 {
1123   struct Plugin *plugin = cls;
1124
1125   GNUNET_assert(cls !=NULL);
1126   plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1127
1128   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1129     return;
1130
1131   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6));
1132   plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
1133   return;
1134 }
1135
1136 static size_t curl_get_header_cb( void *ptr, size_t size, size_t nmemb, void *stream)
1137 {
1138   struct Session * ps = stream;
1139
1140   long http_result = 0;
1141   int res;
1142   /* Getting last http result code */
1143   if (ps->recv_connected==GNUNET_NO)
1144   {
1145     GNUNET_assert(NULL!=ps);
1146     res = curl_easy_getinfo(ps->recv_endpoint, CURLINFO_RESPONSE_CODE, &http_result);
1147     if (CURLE_OK == res)
1148     {
1149       if (http_result == 200)
1150       {
1151         ps->recv_connected = GNUNET_YES;
1152         ps->recv_active = GNUNET_YES;
1153 #if DEBUG_CONNECTIONS
1154         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to recieve data\n",ps);
1155 #endif
1156         // Calling send_check_connections again since receive is established
1157         send_check_connections (ps->peercontext->plugin, ps);
1158       }
1159     }
1160   }
1161
1162 #if DEBUG_CURL
1163   char * tmp;
1164   size_t len = size * nmemb;
1165
1166   tmp = NULL;
1167   if ((size * nmemb) < SIZE_MAX)
1168     tmp = GNUNET_malloc (len+1);
1169
1170   if ((tmp != NULL) && (len > 0))
1171   {
1172     memcpy(tmp,ptr,len);
1173     if (len>=2)
1174     {
1175       if (tmp[len-2] == 13)
1176         tmp[len-2]= '\0';
1177     }
1178 #if DEBUG_CURL
1179     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Header: %s\n",ps,tmp);
1180 #endif
1181   }
1182   if (NULL != tmp)
1183     GNUNET_free (tmp);
1184 #endif
1185
1186   return size * nmemb;
1187 }
1188
1189 static size_t curl_put_header_cb( void *ptr, size_t size, size_t nmemb, void *stream)
1190 {
1191   struct Session * ps = stream;
1192
1193   char * tmp;
1194   size_t len = size * nmemb;
1195   long http_result = 0;
1196   int res;
1197
1198   /* Getting last http result code */
1199   GNUNET_assert(NULL!=ps);
1200   res = curl_easy_getinfo(ps->send_endpoint, CURLINFO_RESPONSE_CODE, &http_result);
1201   if (CURLE_OK == res)
1202   {
1203     if ((http_result == 100) && (ps->send_connected==GNUNET_NO))
1204     {
1205       ps->send_connected = GNUNET_YES;
1206       ps->send_active = GNUNET_YES;
1207 #if DEBUG_CONNECTIONS
1208       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to send data\n",ps);
1209 #endif
1210     }
1211     if ((http_result == 200) && (ps->send_connected==GNUNET_YES))
1212     {
1213       ps->send_connected = GNUNET_NO;
1214       ps->send_active = GNUNET_NO;
1215 #if DEBUG_CONNECTIONS
1216       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: sending disconnected\n",ps);
1217 #endif
1218     }
1219   }
1220
1221   tmp = NULL;
1222   if ((size * nmemb) < SIZE_MAX)
1223     tmp = GNUNET_malloc (len+1);
1224
1225   if ((tmp != NULL) && (len > 0))
1226   {
1227     memcpy(tmp,ptr,len);
1228     if (len>=2)
1229     {
1230       if (tmp[len-2] == 13)
1231         tmp[len-2]= '\0';
1232     }
1233   }
1234   if (NULL != tmp)
1235     GNUNET_free (tmp);
1236
1237   return size * nmemb;
1238 }
1239
1240 /**
1241  * Callback method used with libcurl
1242  * Method is called when libcurl needs to read data during sending
1243  * @param stream pointer where to write data
1244  * @param size size of an individual element
1245  * @param nmemb count of elements that can be written to the buffer
1246  * @param ptr source pointer, passed to the libcurl handle
1247  * @return bytes written to stream
1248  */
1249 static size_t curl_send_cb(void *stream, size_t size, size_t nmemb, void *ptr)
1250 {
1251   struct Session * ps = ptr;
1252   struct HTTP_Message * msg = ps->pending_msgs_tail;
1253   size_t bytes_sent;
1254   size_t len;
1255
1256   if (ps->send_active == GNUNET_NO)
1257         return CURL_READFUNC_PAUSE;
1258
1259
1260   if ((ps->pending_msgs_tail == NULL) && (ps->send_active == GNUNET_YES))
1261   {
1262 #if DEBUG_CONNECTIONS
1263     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: No Message to send, pausing connection\n",ps);
1264 #endif
1265     ps->send_active = GNUNET_NO;
1266     return CURL_READFUNC_PAUSE;
1267   }
1268
1269   msg = ps->pending_msgs_tail;
1270   /* data to send */
1271   if (msg->pos < msg->size)
1272   {
1273     /* data fit in buffer */
1274     if ((msg->size - msg->pos) <= (size * nmemb))
1275     {
1276       len = (msg->size - msg->pos);
1277       memcpy(stream, &msg->buf[msg->pos], len);
1278       msg->pos += len;
1279       bytes_sent = len;
1280     }
1281     else
1282     {
1283       len = size*nmemb;
1284       memcpy(stream, &msg->buf[msg->pos], len);
1285       msg->pos += len;
1286       bytes_sent = len;
1287     }
1288   }
1289   /* no data to send */
1290   else
1291   {
1292     bytes_sent = 0;
1293   }
1294
1295   if ( msg->pos == msg->size)
1296   {
1297 #if DEBUG_CONNECTIONS
1298     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Message with %u bytes sent, removing message from queue \n",ps, msg->pos);
1299 #endif
1300     /* Calling transmit continuation  */
1301     if (( NULL != ps->pending_msgs_tail) && (NULL != ps->pending_msgs_tail->transmit_cont))
1302       msg->transmit_cont (ps->pending_msgs_tail->transmit_cont_cls,&(ps->peercontext)->identity,GNUNET_OK);
1303     remove_http_message(ps, msg);
1304   }
1305   return bytes_sent;
1306 }
1307
1308 static void curl_receive_mst_cb  (void *cls,
1309                                 void *client,
1310                                 const struct GNUNET_MessageHeader *message)
1311 {
1312   struct Session *ps  = cls;
1313   struct HTTP_PeerContext *pc = ps->peercontext;
1314   GNUNET_assert(ps != NULL);
1315   GNUNET_assert(pc != NULL);
1316 #if DEBUG_HTTP
1317   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1318               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
1319               ps,
1320               ntohs(message->type),
1321               ntohs(message->size),
1322               GNUNET_i2s(&(pc->identity)),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen));
1323 #endif
1324   pc->plugin->env->receive (pc->plugin->env->cls,
1325                             &pc->identity,
1326                             message, 1, ps,
1327                             ps->addr,
1328                             ps->addrlen);
1329 }
1330
1331
1332 /**
1333 * Callback method used with libcurl
1334 * Method is called when libcurl needs to write data during sending
1335 * @param stream pointer where to write data
1336 * @param size size of an individual element
1337 * @param nmemb count of elements that can be written to the buffer
1338 * @param ptr destination pointer, passed to the libcurl handle
1339 * @return bytes read from stream
1340 */
1341 static size_t curl_receive_cb( void *stream, size_t size, size_t nmemb, void *ptr)
1342 {
1343   struct Session * ps = ptr;
1344 #if DEBUG_CONNECTIONS
1345   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: %u bytes received\n",ps, size*nmemb);
1346 #endif
1347   GNUNET_SERVER_mst_receive(ps->msgtok, ps, stream, size*nmemb, GNUNET_NO, GNUNET_NO);
1348   return (size * nmemb);
1349
1350 }
1351
1352 static void curl_perform (void *cls,
1353              const struct GNUNET_SCHEDULER_TaskContext *tc)
1354 {
1355   struct Plugin *plugin = cls;
1356   static unsigned int handles_last_run;
1357   int running;
1358   struct CURLMsg *msg;
1359   CURLMcode mret;
1360   struct Session *ps = NULL;
1361   struct HTTP_PeerContext *pc = NULL;
1362   struct HTTP_Message * cur_msg = NULL;
1363   long http_result;
1364   char * tmp;
1365
1366   GNUNET_assert(cls !=NULL);
1367
1368   plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
1369   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1370     return;
1371
1372   do
1373     {
1374       running = 0;
1375       mret = curl_multi_perform (plugin->multi_handle, &running);
1376       if ((running < handles_last_run) && (running>0))
1377         {
1378           do
1379             {
1380
1381               msg = curl_multi_info_read (plugin->multi_handle, &running);
1382               if (running == 0)
1383                   break;
1384               /* get session for affected curl handle */
1385               GNUNET_assert ( msg->easy_handle != NULL );
1386               curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &tmp);
1387               ps = (struct Session *) tmp;
1388               GNUNET_assert ( ps != NULL );
1389               pc = ps->peercontext;
1390               GNUNET_assert ( pc != NULL );
1391               switch (msg->msg)
1392                 {
1393
1394                 case CURLMSG_DONE:
1395                   if ( (msg->data.result != CURLE_OK) &&
1396                        (msg->data.result != CURLE_GOT_NOTHING) )
1397                   {
1398                     /* sending msg failed*/
1399                     if (msg->easy_handle == ps->send_endpoint)
1400                     {
1401 #if DEBUG_CONNECTIONS
1402                       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1403                                  _("Connection %X: HTTPS PUT to peer `%s' (`%s') failed: `%s' `%s'\n"),
1404                                  ps,
1405                                  GNUNET_i2s(&pc->identity),
1406                                  http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1407                                  "curl_multi_perform",
1408                                  curl_easy_strerror (msg->data.result));
1409 #endif
1410                       ps->send_connected = GNUNET_NO;
1411                       ps->send_active = GNUNET_NO;
1412                       curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint);
1413                       //curl_easy_cleanup(ps->send_endpoint);
1414                       //ps->send_endpoint=NULL;
1415                       cur_msg = ps->pending_msgs_tail;
1416                       if (( NULL != cur_msg) && ( NULL != cur_msg->transmit_cont))
1417                         cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR);
1418                     }
1419                     /* GET connection failed */
1420                     if (msg->easy_handle == ps->recv_endpoint)
1421                     {
1422 #if DEBUG_CONNECTIONS
1423                       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1424                            _("Connection %X: HTTPS GET to peer `%s' (`%s') failed: `%s' `%s'\n"),
1425                            ps,
1426                            GNUNET_i2s(&pc->identity),
1427                            http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1428                            "curl_multi_perform",
1429                            curl_easy_strerror (msg->data.result));
1430 #endif
1431                       ps->recv_connected = GNUNET_NO;
1432                       ps->recv_active = GNUNET_NO;
1433                       curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint);
1434                       //curl_easy_cleanup(ps->recv_endpoint);
1435                       //ps->recv_endpoint=NULL;
1436                     }
1437                   }
1438                   else
1439                   {
1440                     if (msg->easy_handle == ps->send_endpoint)
1441                     {
1442                       GNUNET_assert (CURLE_OK == curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_result));
1443 #if DEBUG_CONNECTIONS
1444                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1445                                   "Connection %X: HTTPS PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1446                                    ps,
1447                                    GNUNET_i2s(&pc->identity),
1448                                    http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1449                                    http_result);
1450 #endif
1451                       /* Calling transmit continuation  */
1452                       cur_msg = ps->pending_msgs_tail;
1453                       if (( NULL != cur_msg) && (NULL != cur_msg->transmit_cont))
1454                       {
1455                         /* HTTP 1xx : Last message before here was informational */
1456                         if ((http_result >=100) && (http_result < 200))
1457                           cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
1458                         /* HTTP 2xx: successful operations */
1459                         if ((http_result >=200) && (http_result < 300))
1460                           cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
1461                         /* HTTP 3xx..5xx: error */
1462                         if ((http_result >=300) && (http_result < 600))
1463                           cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR);
1464                       }
1465                       ps->send_connected = GNUNET_NO;
1466                       ps->send_active = GNUNET_NO;
1467                       curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint);
1468                       //curl_easy_cleanup(ps->send_endpoint);
1469                       //ps->send_endpoint =NULL;
1470                     }
1471                     if (msg->easy_handle == ps->recv_endpoint)
1472                     {
1473 #if DEBUG_CONNECTIONS
1474                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1475                                   "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1476                                    ps,
1477                                    GNUNET_i2s(&pc->identity),
1478                                    http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1479                                    http_result);
1480 #endif
1481                       ps->recv_connected = GNUNET_NO;
1482                       ps->recv_active = GNUNET_NO;
1483                       curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint);
1484                       //curl_easy_cleanup(ps->recv_endpoint);
1485                       //ps->recv_endpoint=NULL;
1486                     }
1487                   }
1488                   if ((ps->recv_connected == GNUNET_NO) && (ps->send_connected == GNUNET_NO))
1489                     remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR);
1490                   break;
1491                 default:
1492                   break;
1493                 }
1494
1495             }
1496           while ( (running > 0) );
1497         }
1498       handles_last_run = running;
1499     }
1500   while (mret == CURLM_CALL_MULTI_PERFORM);
1501   curl_schedule(plugin);
1502 }
1503
1504
1505 /**
1506  * Function setting up file descriptors and scheduling task to run
1507  * @param ses session to send data to
1508  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
1509  */
1510 static void
1511 http_plugin_disconnect (void *cls,
1512                             const struct GNUNET_PeerIdentity *target)
1513 {
1514
1515
1516   struct Plugin *plugin = cls;
1517   struct HTTP_PeerContext *pc = NULL;
1518   struct Session *ps = NULL;
1519   //struct Session *tmp = NULL;
1520
1521   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
1522   if (pc==NULL)
1523     return;
1524   ps = pc->head;
1525
1526   while (ps!=NULL)
1527   {
1528     /* Telling transport that session is getting disconnected */
1529     plugin->env->session_end(plugin, target, ps);
1530     if (ps->direction==OUTBOUND)
1531     {
1532       if (ps->send_endpoint!=NULL)
1533       {
1534         //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint));
1535         //curl_easy_cleanup(ps->send_endpoint);
1536         //ps->send_endpoint=NULL;
1537         ps->send_force_disconnect = GNUNET_YES;
1538       }
1539       if (ps->recv_endpoint!=NULL)
1540       {
1541        //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint));
1542        //curl_easy_cleanup(ps->recv_endpoint);
1543        //ps->recv_endpoint=NULL;
1544        ps->recv_force_disconnect = GNUNET_YES;
1545       }
1546     }
1547
1548     if (ps->direction==INBOUND)
1549     {
1550       ps->recv_force_disconnect = GNUNET_YES;
1551       ps->send_force_disconnect = GNUNET_YES;
1552     }
1553
1554     while (ps->pending_msgs_head!=NULL)
1555     {
1556       remove_http_message(ps, ps->pending_msgs_head);
1557     }
1558     ps->recv_active = GNUNET_NO;
1559     ps->send_active = GNUNET_NO;
1560     ps=ps->next;
1561   }
1562 }
1563
1564
1565 static int curl_schedule(void *cls)
1566 {
1567   struct Plugin *plugin = cls;
1568   fd_set rs;
1569   fd_set ws;
1570   fd_set es;
1571   int max;
1572   struct GNUNET_NETWORK_FDSet *grs;
1573   struct GNUNET_NETWORK_FDSet *gws;
1574   long to;
1575   CURLMcode mret;
1576
1577   GNUNET_assert(cls !=NULL);
1578
1579   /* Cancel previous scheduled task */
1580   if (plugin->http_curl_task !=  GNUNET_SCHEDULER_NO_TASK)
1581   {
1582           GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task);
1583           plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
1584   }
1585   max = -1;
1586   FD_ZERO (&rs);
1587   FD_ZERO (&ws);
1588   FD_ZERO (&es);
1589   mret = curl_multi_fdset (plugin->multi_handle, &rs, &ws, &es, &max);
1590   if (mret != CURLM_OK)
1591     {
1592       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1593                   _("%s failed at %s:%d: `%s'\n"),
1594                   "curl_multi_fdset", __FILE__, __LINE__,
1595                   curl_multi_strerror (mret));
1596       return GNUNET_SYSERR;
1597     }
1598   mret = curl_multi_timeout (plugin->multi_handle, &to);
1599   if (mret != CURLM_OK)
1600     {
1601       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1602                   _("%s failed at %s:%d: `%s'\n"),
1603                   "curl_multi_timeout", __FILE__, __LINE__,
1604                   curl_multi_strerror (mret));
1605       return GNUNET_SYSERR;
1606     }
1607
1608   grs = GNUNET_NETWORK_fdset_create ();
1609   gws = GNUNET_NETWORK_fdset_create ();
1610   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1611   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1612   plugin->http_curl_task = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1613                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1614                                    GNUNET_SCHEDULER_NO_TASK,
1615                                    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0),
1616                                    grs,
1617                                    gws,
1618                                    &curl_perform,
1619                                    plugin);
1620   GNUNET_NETWORK_fdset_destroy (gws);
1621   GNUNET_NETWORK_fdset_destroy (grs);
1622   return GNUNET_OK;
1623 }
1624
1625 /**
1626  * Function setting up curl handle and selecting message to send
1627  * @param cls plugin
1628  * @param ses session to send data to
1629  * @param con connection
1630  * @return GNUNET_SYSERR on failure, GNUNET_NO if connecting, GNUNET_YES if ok
1631  */
1632 static ssize_t send_check_connections (void *cls, struct Session *ps)
1633 {
1634   struct Plugin *plugin = cls;
1635   CURLMcode mret;
1636   struct HTTP_Message * msg;
1637
1638   struct GNUNET_TIME_Relative timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
1639
1640   GNUNET_assert(cls !=NULL);
1641
1642   if (ps->direction == OUTBOUND)
1643   {
1644     /* RECV DIRECTION */
1645     /* Check if session is connected to receive data, otherwise connect to peer */
1646     if (ps->recv_connected == GNUNET_NO)
1647     {
1648         int fresh = GNUNET_NO;
1649         if (ps->recv_endpoint == NULL)
1650         {
1651             fresh = GNUNET_YES;
1652                 ps->recv_endpoint = curl_easy_init();
1653         }
1654 #if DEBUG_CURL
1655         curl_easy_setopt(ps->recv_endpoint, CURLOPT_VERBOSE, 1L);
1656 #endif
1657         curl_easy_setopt(ps->recv_endpoint, CURLOPT_URL, ps->url);
1658         curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1659         //curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSL_CIPHER_LIST, cipher_suite);
1660                 curl_easy_setopt(ps->recv_endpoint, CURLOPT_SSL_VERIFYPEER, 0);
1661                 curl_easy_setopt(ps->recv_endpoint, CURLOPT_SSL_VERIFYHOST, 0);
1662         curl_easy_setopt(ps->recv_endpoint, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
1663         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEHEADER, ps);
1664         curl_easy_setopt(ps->recv_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
1665         curl_easy_setopt(ps->recv_endpoint, CURLOPT_READDATA, ps);
1666         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb);
1667         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEDATA, ps);
1668         curl_easy_setopt(ps->recv_endpoint, CURLOPT_TIMEOUT, (long) timeout.value);
1669         curl_easy_setopt(ps->recv_endpoint, CURLOPT_PRIVATE, ps);
1670         curl_easy_setopt(ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
1671         curl_easy_setopt(ps->recv_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
1672
1673         if (fresh==GNUNET_YES)
1674         {
1675                         mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint);
1676                         if (mret != CURLM_OK)
1677                         {
1678                           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1679                                                   _("Connection: %X: %s failed at %s:%d: `%s'\n"),
1680                                                   ps,
1681                                                   "curl_multi_add_handle", __FILE__, __LINE__,
1682                                                   curl_multi_strerror (mret));
1683                           return GNUNET_SYSERR;
1684                         }
1685         }
1686         if (curl_schedule (plugin) == GNUNET_SYSERR)
1687         {
1688 #if DEBUG_CONNECTIONS
1689         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: could not schedule curl task\n",ps);
1690 #endif
1691                 return GNUNET_SYSERR;
1692         }
1693 #if DEBUG_CONNECTIONS
1694         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound not connected, initiating connection\n",ps);
1695 #endif
1696     }
1697
1698     /* waiting for receive direction */
1699     if (ps->recv_connected==GNUNET_NO)
1700       return GNUNET_NO;
1701
1702     /* SEND DIRECTION */
1703     /* Check if session is connected to send data, otherwise connect to peer */
1704     if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint!= NULL))
1705     {
1706       if (ps->send_active == GNUNET_YES)
1707       {
1708 #if DEBUG_CONNECTIONS
1709         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound active, enqueueing message\n",ps);
1710 #endif
1711         return GNUNET_YES;
1712       }
1713       if (ps->send_active == GNUNET_NO)
1714       {
1715 #if DEBUG_CONNECTIONS
1716         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",ps);
1717 #endif
1718         if (CURLE_OK == curl_easy_pause(ps->send_endpoint,CURLPAUSE_CONT))
1719         {
1720                         ps->send_active=GNUNET_YES;
1721                         return GNUNET_YES;
1722         }
1723         else
1724                 return GNUNET_SYSERR;
1725       }
1726     }
1727     /* not connected, initiate connection */
1728     if (ps->send_connected==GNUNET_NO)
1729     {
1730         int fresh = GNUNET_NO;
1731         if (NULL == ps->send_endpoint)
1732         {
1733                 ps->send_endpoint = curl_easy_init();
1734                 fresh = GNUNET_YES;
1735         }
1736                 GNUNET_assert (ps->send_endpoint != NULL);
1737                 GNUNET_assert (NULL != ps->pending_msgs_tail);
1738 #if DEBUG_CONNECTIONS
1739                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound not connected, initiating connection\n",ps);
1740 #endif
1741                 ps->send_active = GNUNET_NO;
1742                 msg = ps->pending_msgs_tail;
1743
1744 #if DEBUG_CURL
1745                 curl_easy_setopt(ps->send_endpoint, CURLOPT_VERBOSE, 1L);
1746 #endif
1747                 curl_easy_setopt(ps->send_endpoint, CURLOPT_URL, ps->url);
1748                 curl_easy_setopt(ps->send_endpoint, CURLOPT_PUT, 1L);
1749                 curl_easy_setopt(ps->send_endpoint, CURLOPT_HEADERFUNCTION, &curl_put_header_cb);
1750                 curl_easy_setopt(ps->send_endpoint, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1751                 curl_easy_setopt(ps->send_endpoint, CURLOPT_SSL_VERIFYPEER, 0);
1752                 curl_easy_setopt(ps->send_endpoint, CURLOPT_SSL_VERIFYHOST, 0);
1753
1754                 curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEHEADER, ps);
1755                 curl_easy_setopt(ps->send_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
1756                 curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
1757                 curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb);
1758                 curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
1759                 curl_easy_setopt(ps->send_endpoint, CURLOPT_TIMEOUT, (long) timeout.value);
1760                 curl_easy_setopt(ps->send_endpoint, CURLOPT_PRIVATE, ps);
1761                 curl_easy_setopt(ps->send_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
1762                 curl_easy_setopt(ps->send_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
1763
1764                 if (fresh==GNUNET_YES)
1765                 {
1766                         mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint);
1767                         if (mret != CURLM_OK)
1768                         {
1769                           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1770                                                   _("Connection: %X: %s failed at %s:%d: `%s'\n"),
1771                                                   ps,
1772                                                   "curl_multi_add_handle", __FILE__, __LINE__,
1773                                                   curl_multi_strerror (mret));
1774                           return GNUNET_SYSERR;
1775                         }
1776                 }
1777     }
1778     if (curl_schedule (plugin) == GNUNET_SYSERR)
1779         return GNUNET_SYSERR;
1780     return GNUNET_YES;
1781   }
1782   if (ps->direction == INBOUND)
1783   {
1784     GNUNET_assert (NULL != ps->pending_msgs_tail);
1785     if ((ps->recv_connected==GNUNET_YES) && (ps->send_connected==GNUNET_YES) &&
1786         (ps->recv_force_disconnect==GNUNET_NO) && (ps->recv_force_disconnect==GNUNET_NO))
1787         return GNUNET_YES;
1788   }
1789   return GNUNET_SYSERR;
1790 }
1791
1792 static struct Session * send_select_session (void * cls, struct HTTP_PeerContext *pc, const void * addr, size_t addrlen, int force_address, struct Session * session)
1793 {
1794         struct Session * tmp = NULL;
1795         int addr_given = GNUNET_NO;
1796
1797         if ((addr!=NULL) && (addrlen>0))
1798                 addr_given = GNUNET_YES;
1799
1800         if (force_address == GNUNET_YES)
1801         {
1802                 /* check session given as argument */
1803                 if ((session != NULL) && (addr_given == GNUNET_YES))
1804                 {
1805                       if (0 == memcmp(session->addr, addr, addrlen))
1806                       {
1807                         /* connection can not be used, since it is disconnected */
1808                         if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO))
1809                         {
1810 #if DEBUG_SESSION_SELECTION
1811                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send to forced address \n", session);
1812 #endif
1813                                 return session;
1814                         }
1815                       }
1816                 }
1817                 /* check last session used */
1818                 if ((pc->last_session != NULL)&& (addr_given == GNUNET_YES))
1819                 {
1820                       if (0 == memcmp(pc->last_session->addr, addr, addrlen))
1821                       {
1822                         /* connection can not be used, since it is disconnected */
1823                         if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO))
1824                         {
1825 #if DEBUG_SESSION_SELECTION
1826                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session used to send to forced address \n", pc->last_session);
1827 #endif
1828                                 return pc->last_session;
1829                         }
1830                       }
1831                 }
1832                 /* find session in existing sessions */
1833                 tmp = pc->head;
1834                 while ((tmp!=NULL) && (addr_given == GNUNET_YES))
1835                 {
1836
1837                           if (0 == memcmp(tmp->addr, addr, addrlen))
1838                       {
1839                         /* connection can not be used, since it is disconnected */
1840                         if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO))
1841                         {
1842 #if DEBUG_SESSION_SELECTION
1843                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to forced address \n", session);
1844 #endif
1845                                   return session;
1846                         }
1847
1848                       }
1849                           tmp=tmp->next;
1850                 }
1851                 /* no session to use */
1852                 return NULL;
1853         }
1854         if ((force_address == GNUNET_NO) || (force_address == GNUNET_SYSERR))
1855         {
1856                 /* check session given as argument */
1857                 if (session != NULL)
1858                 {
1859                         /* connection can not be used, since it is disconnected */
1860                         if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO))
1861                         {
1862 #if DEBUG_SESSION_SELECTION
1863                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send not-forced address \n", session);
1864 #endif
1865                                   return session;
1866                         }
1867
1868                 }
1869                 /* check last session used */
1870                 if (pc->last_session != NULL)
1871                 {
1872                         /* connection can not be used, since it is disconnected */
1873                         if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO))
1874                         {
1875 #if DEBUG_SESSION_SELECTION
1876                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session to send to not-forced address \n", pc->last_session);
1877 #endif
1878                                 return pc->last_session;
1879                         }
1880                 }
1881                 /* find session in existing sessions */
1882                 tmp = pc->head;
1883                 while (tmp!=NULL)
1884                 {
1885                         /* connection can not be used, since it is disconnected */
1886                         if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO))
1887                         {
1888 #if DEBUG_SESSION_SELECTION
1889                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to not-forced address \n", tmp);
1890 #endif
1891                                 return tmp;
1892                         }
1893                         tmp=tmp->next;
1894                 }
1895                 return NULL;
1896         }
1897         return NULL;
1898 }
1899
1900 /**
1901  * Function that can be used by the transport service to transmit
1902  * a message using the plugin.   Note that in the case of a
1903  * peer disconnecting, the continuation MUST be called
1904  * prior to the disconnect notification itself.  This function
1905  * will be called with this peer's HELLO message to initiate
1906  * a fresh connection to another peer.
1907  *
1908  * @param cls closure
1909  * @param target who should receive this message
1910  * @param msgbuf the message to transmit
1911  * @param msgbuf_size number of bytes in 'msgbuf'
1912  * @param priority how important is the message (most plugins will
1913  *                 ignore message priority and just FIFO)
1914  * @param timeout how long to wait at most for the transmission (does not
1915  *                require plugins to discard the message after the timeout,
1916  *                just advisory for the desired delay; most plugins will ignore
1917  *                this as well)
1918  * @param session which session must be used (or NULL for "any")
1919  * @param addr the address to use (can be NULL if the plugin
1920  *                is "on its own" (i.e. re-use existing TCP connection))
1921  * @param addrlen length of the address in bytes
1922  * @param force_address GNUNET_YES if the plugin MUST use the given address,
1923  *                GNUNET_NO means the plugin may use any other address and
1924  *                GNUNET_SYSERR means that only reliable existing
1925  *                bi-directional connections should be used (regardless
1926  *                of address)
1927  * @param cont continuation to call once the message has
1928  *        been transmitted (or if the transport is ready
1929  *        for the next transmission call; or if the
1930  *        peer disconnected...); can be NULL
1931  * @param cont_cls closure for cont
1932  * @return number of bytes used (on the physical network, with overheads);
1933  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
1934  *         and does NOT mean that the message was not transmitted (DV)
1935  */
1936 static ssize_t
1937 http_plugin_send (void *cls,
1938                   const struct GNUNET_PeerIdentity *target,
1939                   const char *msgbuf,
1940                   size_t msgbuf_size,
1941                   unsigned int priority,
1942                   struct GNUNET_TIME_Relative to,
1943                   struct Session *session,
1944                   const void *addr,
1945                   size_t addrlen,
1946                   int force_address,
1947                   GNUNET_TRANSPORT_TransmitContinuation cont,
1948                   void *cont_cls)
1949 {
1950   struct Plugin *plugin = cls;
1951   struct HTTP_Message *msg;
1952   struct HTTP_PeerContext * pc;
1953   struct Session * ps = NULL;
1954
1955   GNUNET_assert(cls !=NULL);
1956
1957 #if DEBUG_HTTP
1958   char * force = GNUNET_malloc(40);
1959   if (force_address == GNUNET_YES)
1960     strcpy(force,"forced addr.");
1961   if (force_address == GNUNET_NO)
1962     strcpy(force,"any addr.");
1963   if (force_address == GNUNET_SYSERR)
1964     strcpy(force,"reliable bi-direc. address addr.");
1965
1966   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport tells me to send %u bytes to `%s' using %s (%s) and session: %X\n",
1967                                       msgbuf_size,
1968                                       GNUNET_i2s(target),
1969                                       force,
1970                                       http_plugin_address_to_string(NULL, addr, addrlen),
1971                                       session);
1972
1973   GNUNET_free(force);
1974 #endif
1975
1976   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
1977   /* Peer unknown */
1978   if (pc==NULL)
1979   {
1980     pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext));
1981     pc->plugin = plugin;
1982     pc->session_id_counter=1;
1983     pc->last_session = NULL;
1984     memcpy(&pc->identity, target, sizeof(struct GNUNET_PeerIdentity));
1985     GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1986     GNUNET_STATISTICS_update (plugin->env->stats,
1987                             gettext_noop ("# HTTP peers active"),
1988                             1,
1989                             GNUNET_NO);
1990   }
1991
1992   ps = send_select_session (plugin, pc, addr, addrlen, force_address, session);
1993
1994   /* session not existing, but address forced -> creating new session */
1995   if (ps==NULL)
1996   {
1997     if ((addr!=NULL) && (addrlen!=0))
1998     {
1999       ps = GNUNET_malloc(sizeof (struct Session));
2000 #if DEBUG_SESSION_SELECTION
2001       if (force_address == GNUNET_YES)
2002         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection & forced address: creating new session %X to peer %s\n", ps, GNUNET_i2s(target));
2003       if (force_address != GNUNET_YES)
2004         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection: creating new session %X to peer %s\n", ps, GNUNET_i2s(target));
2005 #endif
2006       if ((addrlen!=0) && (addr!=NULL))
2007       {
2008       ps->addr = GNUNET_malloc(addrlen);
2009       memcpy(ps->addr,addr,addrlen);
2010       ps->addrlen = addrlen;
2011       }
2012       else
2013       {
2014         ps->addr = NULL;
2015         ps->addrlen = 0;
2016       }
2017       ps->direction=OUTBOUND;
2018       ps->recv_connected = GNUNET_NO;
2019       ps->recv_force_disconnect = GNUNET_NO;
2020       ps->send_connected = GNUNET_NO;
2021       ps->send_force_disconnect = GNUNET_NO;
2022       ps->pending_msgs_head = NULL;
2023       ps->pending_msgs_tail = NULL;
2024       ps->peercontext=pc;
2025       ps->session_id = pc->session_id_counter;
2026       pc->session_id_counter++;
2027       ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
2028       if (ps->msgtok == NULL)
2029         ps->msgtok = GNUNET_SERVER_mst_create (&curl_receive_mst_cb, ps);
2030       GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
2031 /* FIXME */
2032
2033       GNUNET_STATISTICS_update (plugin->env->stats,
2034                             gettext_noop ("# HTTP outbound sessions for peers active"),
2035                             1,
2036                             GNUNET_NO);
2037     }
2038     else
2039     {
2040 #if DEBUG_HTTP
2041       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing session found & and no address given: no way to send this message to peer `%s'!\n", GNUNET_i2s(target));
2042 #endif
2043       return GNUNET_SYSERR;
2044     }
2045   }
2046
2047   /* create msg */
2048   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
2049   msg->next = NULL;
2050   msg->size = msgbuf_size;
2051   msg->pos = 0;
2052   msg->buf = (char *) &msg[1];
2053   msg->transmit_cont = cont;
2054   msg->transmit_cont_cls = cont_cls;
2055   memcpy (msg->buf,msgbuf, msgbuf_size);
2056   GNUNET_CONTAINER_DLL_insert(ps->pending_msgs_head,ps->pending_msgs_tail,msg);
2057
2058   if (send_check_connections (plugin, ps) != GNUNET_SYSERR)
2059   {
2060           if (force_address != GNUNET_YES)
2061                   pc->last_session = ps;
2062
2063           if (pc->last_session==NULL)
2064                   pc->last_session = ps;
2065           return msg->size;
2066   }
2067   else
2068           return GNUNET_SYSERR;
2069 }
2070
2071
2072
2073 /**
2074  * Function that can be used to force the plugin to disconnect
2075  * from the given peer and cancel all previous transmissions
2076  * (and their continuationc).
2077  *
2078  * @param cls closure
2079  * @param target peer from which to disconnect
2080  */
2081 /**
2082  * Convert the transports address to a nice, human-readable
2083  * format.
2084  *
2085  * @param cls closure
2086  * @param type name of the transport that generated the address
2087  * @param addr one of the addresses of the host, NULL for the last address
2088  *        the specific address format depends on the transport
2089  * @param addrlen length of the address
2090  * @param numeric should (IP) addresses be displayed in numeric form?
2091  * @param timeout after how long should we give up?
2092  * @param asc function to call on each string
2093  * @param asc_cls closure for asc
2094  */
2095 static void
2096 http_plugin_address_pretty_printer (void *cls,
2097                                         const char *type,
2098                                         const void *addr,
2099                                         size_t addrlen,
2100                                         int numeric,
2101                                         struct GNUNET_TIME_Relative timeout,
2102                                         GNUNET_TRANSPORT_AddressStringCallback
2103                                         asc, void *asc_cls)
2104 {
2105   const struct IPv4HttpAddress *t4;
2106   const struct IPv6HttpAddress *t6;
2107   struct sockaddr_in a4;
2108   struct sockaddr_in6 a6;
2109   char * address;
2110   char * ret;
2111   unsigned int port;
2112   unsigned int res;
2113
2114   GNUNET_assert(cls !=NULL);
2115   if (addrlen == sizeof (struct IPv6HttpAddress))
2116   {
2117     address = GNUNET_malloc (INET6_ADDRSTRLEN);
2118     t6 = addr;
2119     a6.sin6_addr = t6->ipv6_addr;
2120     inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
2121     port = ntohs(t6->u6_port);
2122   }
2123   else if (addrlen == sizeof (struct IPv4HttpAddress))
2124   {
2125     address = GNUNET_malloc (INET_ADDRSTRLEN);
2126     t4 = addr;
2127     a4.sin_addr.s_addr =  t4->ipv4_addr;
2128     inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
2129     port = ntohs(t4->u_port);
2130   }
2131   else
2132   {
2133     /* invalid address */
2134     GNUNET_break_op (0);
2135     asc (asc_cls, NULL);
2136     return;
2137   }
2138   res = GNUNET_asprintf(&ret,"%s://%s:%u/", PROTOCOL_PREFIX, address,port);
2139   GNUNET_free (address);
2140   GNUNET_assert(res != 0);
2141   asc (asc_cls, ret);
2142   GNUNET_free_non_null (ret);
2143 }
2144
2145
2146
2147 /**
2148  * Another peer has suggested an address for this
2149  * peer and transport plugin.  Check that this could be a valid
2150  * address.  If so, consider adding it to the list
2151  * of addresses.
2152  *
2153  * @param cls closure
2154  * @param addr pointer to the address
2155  * @param addrlen length of addr
2156  * @return GNUNET_OK if this is a plausible address for this peer
2157  *         and transport
2158  */
2159 static int
2160 http_plugin_address_suggested (void *cls,
2161                                const void *addr, size_t addrlen)
2162 {
2163   struct Plugin *plugin = cls;
2164   struct IPv4HttpAddress *v4;
2165   struct IPv6HttpAddress *v6;
2166   unsigned int port;
2167
2168   GNUNET_assert(cls !=NULL);
2169   if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
2170       (addrlen != sizeof (struct IPv6HttpAddress)))
2171     {
2172       return GNUNET_SYSERR;
2173     }
2174   if (addrlen == sizeof (struct IPv4HttpAddress))
2175     {
2176       v4 = (struct IPv4HttpAddress *) addr;
2177       /* Not skipping loopback
2178       if (INADDR_LOOPBACK == ntohl(v4->ipv4_addr))
2179       {
2180         return GNUNET_SYSERR;
2181       } */
2182       port = ntohs (v4->u_port);
2183       if (port != plugin->port_inbound)
2184       {
2185         return GNUNET_SYSERR;
2186       }
2187     }
2188   if (addrlen == sizeof (struct IPv6HttpAddress))
2189     {
2190       v6 = (struct IPv6HttpAddress *) addr;
2191       if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
2192         {
2193           return GNUNET_SYSERR;
2194         }
2195       port = ntohs (v6->u6_port);
2196       if (port != plugin->port_inbound)
2197       {
2198         return GNUNET_SYSERR;
2199       }
2200     }
2201
2202   return GNUNET_OK;
2203 }
2204
2205
2206 /**
2207  * Function called for a quick conversion of the binary address to
2208  * a numeric address.  Note that the caller must not free the
2209  * address and that the next call to this function is allowed
2210  * to override the address again.
2211  *
2212  * @param cls closure
2213  * @param addr binary address
2214  * @param addrlen length of the address
2215  * @return string representing the same address
2216  */
2217 static const char*
2218 http_plugin_address_to_string (void *cls,
2219                                    const void *addr,
2220                                    size_t addrlen)
2221 {
2222   const struct IPv4HttpAddress *t4;
2223   const struct IPv6HttpAddress *t6;
2224   struct sockaddr_in a4;
2225   struct sockaddr_in6 a6;
2226   char * address;
2227   char * ret;
2228   uint16_t port;
2229   unsigned int res;
2230
2231   if (addrlen == sizeof (struct IPv6HttpAddress))
2232     {
2233       address = GNUNET_malloc (INET6_ADDRSTRLEN);
2234       t6 = addr;
2235       a6.sin6_addr = t6->ipv6_addr;
2236       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
2237       port = ntohs(t6->u6_port);
2238     }
2239   else if (addrlen == sizeof (struct IPv4HttpAddress))
2240     {
2241       address = GNUNET_malloc (INET_ADDRSTRLEN);
2242       t4 = addr;
2243       a4.sin_addr.s_addr =  t4->ipv4_addr;
2244       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
2245       port = ntohs(t4->u_port);
2246     }
2247   else
2248     {
2249       /* invalid address */
2250       return NULL;
2251     }
2252   res = GNUNET_asprintf(&ret,"%s:%u",address,port);
2253   GNUNET_free (address);
2254   GNUNET_assert(res != 0);
2255   return ret;
2256 }
2257
2258 static char *
2259 load_certificate( const char * file )
2260 {
2261   struct GNUNET_DISK_FileHandle * gn_file;
2262
2263   struct stat fstat;
2264   char * text = NULL;
2265
2266   if (0!=STAT(file, &fstat))
2267           return NULL;
2268   text = GNUNET_malloc (fstat.st_size);
2269   gn_file = GNUNET_DISK_file_open(file,GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_USER_READ);
2270   if (gn_file==NULL)
2271   {
2272           GNUNET_free(text);
2273           return NULL;
2274   }
2275   if (GNUNET_SYSERR == GNUNET_DISK_file_read(gn_file, text, fstat.st_size))
2276   {
2277           GNUNET_free(text);
2278           return NULL;
2279   }
2280   text[fstat.st_size] = '\0';
2281   GNUNET_DISK_file_close(gn_file);
2282
2283   return text;
2284 }
2285
2286
2287 /**
2288  * Exit point from the plugin.
2289  */
2290 void *
2291 libgnunet_plugin_transport_https_done (void *cls)
2292 {
2293   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2294   struct Plugin *plugin = api->cls;
2295   CURLMcode mret;
2296   GNUNET_assert(cls !=NULL);
2297
2298   if (plugin->http_server_daemon_v4 != NULL)
2299   {
2300     MHD_stop_daemon (plugin->http_server_daemon_v4);
2301     plugin->http_server_daemon_v4 = NULL;
2302   }
2303   if (plugin->http_server_daemon_v6 != NULL)
2304   {
2305     MHD_stop_daemon (plugin->http_server_daemon_v6);
2306     plugin->http_server_daemon_v6 = NULL;
2307   }
2308
2309   if ( plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
2310   {
2311     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4);
2312     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
2313   }
2314
2315   if ( plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
2316   {
2317     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6);
2318     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
2319   }
2320
2321
2322   /* free all peer information */
2323   if (plugin->peers!=NULL)
2324   {
2325           GNUNET_CONTAINER_multihashmap_iterate (plugin->peers,
2326                                                                                          &remove_peer_context_Iterator,
2327                                                                                          plugin);
2328           GNUNET_CONTAINER_multihashmap_destroy (plugin->peers);
2329   }
2330   if (plugin->multi_handle!=NULL)
2331   {
2332           mret = curl_multi_cleanup(plugin->multi_handle);
2333 #if DEBUG_HTTP
2334           if ( CURLM_OK != mret)
2335                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed\n");
2336 #endif
2337           plugin->multi_handle = NULL;
2338   }
2339   curl_global_cleanup();
2340
2341   if ( plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2342   {
2343     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task);
2344     plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2345   }
2346
2347   GNUNET_free_non_null (plugin->bind4_address);
2348   GNUNET_free_non_null (plugin->bind6_address);
2349   GNUNET_free_non_null(plugin->bind_hostname);
2350   GNUNET_free (plugin);
2351   GNUNET_free (api);
2352 #if DEBUG_HTTP
2353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unload http plugin complete...\n");
2354 #endif
2355   return NULL;
2356 }
2357
2358
2359 /**
2360  * Entry point for the plugin.
2361  */
2362 void *
2363 libgnunet_plugin_transport_https_init (void *cls)
2364 {
2365   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2366   struct Plugin *plugin;
2367   struct GNUNET_TRANSPORT_PluginFunctions *api;
2368   struct GNUNET_TIME_Relative gn_timeout;
2369   long long unsigned int port;
2370
2371   char * key_file;
2372   char * cert_file;
2373
2374   GNUNET_assert(cls !=NULL);
2375 #if DEBUG_HTTP
2376   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting https plugin...\n");
2377 #endif
2378
2379   plugin = GNUNET_malloc (sizeof (struct Plugin));
2380   plugin->stats = env->stats;
2381   plugin->env = env;
2382   plugin->peers = NULL;
2383   plugin->bind4_address = NULL;
2384   plugin->use_ipv6  = GNUNET_YES;
2385   plugin->use_ipv4  = GNUNET_YES;
2386
2387   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2388   api->cls = plugin;
2389   api->send = &http_plugin_send;
2390   api->disconnect = &http_plugin_disconnect;
2391   api->address_pretty_printer = &http_plugin_address_pretty_printer;
2392   api->check_address = &http_plugin_address_suggested;
2393   api->address_to_string = &http_plugin_address_to_string;
2394
2395   /* Hashing our identity to use it in URLs */
2396   GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &plugin->my_ascii_hash_ident);
2397
2398   /* Use IPv6 yes/no */
2399   if (GNUNET_CONFIGURATION_have_value (env->cfg,
2400                                                                    "transport-https", "USE_IPv6"))
2401     {
2402           plugin->use_ipv6 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2403                                                                                                            "transport-https",
2404                                                                                                            "USE_IPv6");
2405     }
2406   /* Use IPv4 yes/no */
2407   if (GNUNET_CONFIGURATION_have_value (env->cfg,
2408                                                                    "transport-https", "USE_IPv4"))
2409     {
2410           plugin->use_ipv4 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2411                                                                                                            "transport-https",
2412                                                                                                            "USE_IPv4");
2413     }
2414   /* Reading port number from config file */
2415   if ((GNUNET_OK !=
2416        GNUNET_CONFIGURATION_get_value_number (env->cfg,
2417                                               "transport-https",
2418                                               "PORT",
2419                                               &port)) ||
2420       (port > 65535) )
2421     {
2422       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2423                        "http",
2424                        _("Require valid port number for transport plugin `%s' in configuration!\n"),
2425                        "transport-https");
2426       libgnunet_plugin_transport_https_done (api);
2427       return NULL;
2428     }
2429
2430   /* Reading ipv4 addresse to bind to from config file */
2431   if ((plugin->use_ipv4==GNUNET_YES) && (GNUNET_CONFIGURATION_have_value (env->cfg,
2432                                                                    "transport-https", "BINDTO4")))
2433   {
2434           GNUNET_break (GNUNET_OK ==
2435                                         GNUNET_CONFIGURATION_get_value_string (env->cfg,
2436                                                                                                                    "transport-https",
2437                                                                                                                    "BINDTO4",
2438                                                                                                                    &plugin->bind_hostname));
2439           plugin->bind4_address = GNUNET_malloc(sizeof(struct sockaddr_in));
2440           plugin->bind4_address->sin_family = AF_INET;
2441           plugin->bind4_address->sin_port = htons (port);
2442
2443           if (inet_pton(AF_INET,plugin->bind_hostname, &plugin->bind4_address->sin_addr)<=0)
2444           {
2445                   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2446                                                    "http",
2447                                                    _("Misconfigured address to bind to in configuration!\n"),
2448                                                    "transport-https");
2449                   GNUNET_free(plugin->bind4_address);
2450                   GNUNET_free(plugin->bind_hostname);
2451                   plugin->bind_hostname = NULL;
2452                   plugin->bind4_address = NULL;
2453           }
2454   }
2455
2456   /* Get private key file from config */
2457   if (GNUNET_CONFIGURATION_have_value (env->cfg,
2458                                                                            "transport-https", "KEY_FILE"))
2459   {
2460                 GNUNET_CONFIGURATION_get_value_string (env->cfg,
2461                                                                                            "transport-https",
2462                                                                                            "KEY_FILE",
2463                                                                                            &key_file);
2464   }
2465   else
2466   {
2467           GNUNET_asprintf(&key_file,"https.key");
2468   }
2469
2470   /* Get private key file from config */
2471   if (GNUNET_CONFIGURATION_have_value (env->cfg,
2472                                                                            "transport-https", "CERT_FILE"))
2473   {
2474           GNUNET_CONFIGURATION_get_value_string (env->cfg,
2475                                                                                          "transport-https",
2476                                                                                      "CERT_FILE",
2477                                                                                      &cert_file);
2478   }
2479   else
2480   {
2481           GNUNET_asprintf(&cert_file,"https.cert");
2482   }
2483
2484   /* Reading ipv4 addresse to bind to from config file */
2485   if ((plugin->use_ipv6==GNUNET_YES) && (GNUNET_CONFIGURATION_have_value (env->cfg,
2486                                                                    "transport-https", "BINDTO6")))
2487   {
2488           GNUNET_break (GNUNET_OK ==
2489                                         GNUNET_CONFIGURATION_get_value_string (env->cfg,
2490                                                                                                                    "transport-https",
2491                                                                                                                    "BINDTO6",
2492                                                                                                                    &plugin->bind_hostname));
2493
2494           plugin->bind6_address = GNUNET_malloc(sizeof(struct sockaddr_in6));
2495           plugin->bind6_address->sin6_family = AF_INET6;
2496           plugin->bind6_address->sin6_port = htons (port);
2497
2498       if (inet_pton(AF_INET6,plugin->bind_hostname, &plugin->bind6_address->sin6_addr)<=0)
2499           {
2500                   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2501                                                    "http",
2502                                                    _("Misconfigured address to bind to in configuration!\n"),
2503                                                    "transport-https");
2504                   GNUNET_free(plugin->bind6_address);
2505                   GNUNET_free(plugin->bind_hostname);
2506                   plugin->bind_hostname = NULL;
2507                   plugin->bind6_address = NULL;
2508           }
2509   }
2510
2511   /* read key & certificates from file */
2512   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading TLS certificate `%s' `%s'\n", key_file, cert_file);
2513
2514   plugin->key = load_certificate( key_file );
2515   plugin->cert = load_certificate( cert_file );
2516
2517   if ((plugin->key==NULL) || (plugin->cert==NULL))
2518   {
2519           char * cmd;
2520           int ret = 0;
2521           GNUNET_asprintf(&cmd,"gnunet-transport-certificate-creation %s %s", key_file, cert_file);
2522           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No usable TLS certificate found, creating certificate \n");
2523           ret = system(cmd);
2524
2525           if (ret != 0)
2526           {
2527                   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2528                                            "https",
2529                                                    _("Could not create a new TLS certificate, shell script `%s' failed!\n"),cmd,
2530                                                    "transport-https");
2531                   GNUNET_free (key_file);
2532                   GNUNET_free (cert_file);
2533                   libgnunet_plugin_transport_https_done(api);
2534                   GNUNET_free (cmd);
2535                   return NULL;
2536           }
2537
2538           GNUNET_free (cmd);
2539
2540           plugin->key = load_certificate( key_file );
2541           plugin->cert = load_certificate( cert_file );
2542
2543           if ((plugin->key==NULL) || (plugin->cert==NULL))
2544           {
2545                   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2546                                            "https",
2547                                                    _("No usable TLS certificate found and creating one failed! \n"),
2548                                                    "transport-https");
2549                   GNUNET_free (key_file);
2550                   GNUNET_free (cert_file);
2551                   libgnunet_plugin_transport_https_done(api);
2552                   return NULL;
2553           }
2554   }
2555
2556   GNUNET_free (key_file);
2557   GNUNET_free (cert_file);
2558
2559
2560   GNUNET_assert((plugin->key!=NULL) && (plugin->cert!=NULL));
2561   GNUNET_assert ((port > 0) && (port <= 65535));
2562   plugin->port_inbound = port;
2563   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
2564   unsigned int timeout = (gn_timeout.value) / 1000;
2565   if ((plugin->http_server_daemon_v6 == NULL) && (plugin->use_ipv6 == GNUNET_YES) && (port != 0))
2566   {
2567         struct sockaddr * tmp = (struct sockaddr *) plugin->bind6_address;
2568     plugin->http_server_daemon_v6 = MHD_start_daemon (
2569 #if DEBUG_CONNECTIONS
2570                                                                    MHD_USE_DEBUG |
2571 #endif
2572                                                                    MHD_USE_IPv6 | MHD_USE_SSL,
2573                                        port,
2574                                        &mhd_accept_cb,
2575                                        plugin , &mdh_access_cb, plugin,
2576                                        MHD_OPTION_HTTPS_MEM_KEY, plugin->key,
2577                                        MHD_OPTION_HTTPS_MEM_CERT, plugin->cert,
2578                                        MHD_OPTION_SOCK_ADDR, tmp,
2579                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32,
2580                                        //MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6,
2581                                        MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout,
2582                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
2583                                        MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL,
2584                                        MHD_OPTION_END);
2585   }
2586   if ((plugin->http_server_daemon_v4 == NULL) && (plugin->use_ipv4 == GNUNET_YES) && (port != 0))
2587   {
2588   plugin->http_server_daemon_v4 = MHD_start_daemon (
2589 #if DEBUG_CONNECTIONS
2590                                                                    MHD_USE_DEBUG |
2591 #endif
2592                                                                    MHD_NO_FLAG | MHD_USE_SSL,
2593                                        port,
2594                                        &mhd_accept_cb,
2595                                        plugin , &mdh_access_cb, plugin,
2596                                        MHD_OPTION_HTTPS_MEM_KEY, plugin->key,
2597                                        MHD_OPTION_HTTPS_MEM_CERT, plugin->cert,
2598                                        MHD_OPTION_SOCK_ADDR, (struct sockaddr_in *)plugin->bind4_address,
2599                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32,
2600                                        //MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6,
2601                                        MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout,
2602                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
2603                                        MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL,
2604                                        MHD_OPTION_END);
2605   }
2606   if (plugin->http_server_daemon_v4 != NULL)
2607     plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
2608   if (plugin->http_server_daemon_v6 != NULL)
2609     plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
2610
2611
2612   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
2613   {
2614 #if DEBUG_HTTP
2615           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address",port);
2616 #endif
2617   }
2618   else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) && (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK))
2619   {
2620 #if DEBUG_HTTP
2621     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv6 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address", port);
2622 #endif
2623   }
2624   else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) && (plugin->http_server_task_v4 == GNUNET_SCHEDULER_NO_TASK))
2625   {
2626 #if DEBUG_HTTP
2627     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address", port);
2628 #endif
2629   }
2630   else
2631   {
2632 #if DEBUG_HTTP
2633     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No MHD was started, transport plugin not functional!\n");
2634 #endif
2635     libgnunet_plugin_transport_https_done (api);
2636     return NULL;
2637   }
2638
2639   /* Initializing cURL */
2640   curl_global_init(CURL_GLOBAL_ALL);
2641   plugin->multi_handle = curl_multi_init();
2642
2643   if ( NULL == plugin->multi_handle )
2644   {
2645     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2646                                    "http",
2647                                    _("Could not initialize curl multi handle, failed to start http plugin!\n"),
2648                                    "transport-https");
2649     libgnunet_plugin_transport_https_done (api);
2650     return NULL;
2651   }
2652
2653   plugin->peers = GNUNET_CONTAINER_multihashmap_create (10);
2654   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
2655
2656   return api;
2657 }
2658
2659 /* end of plugin_transport_http.c */