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