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