(no commit message)
[oweals/gnunet.git] / src / transport / plugin_transport_http.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/plugin_transport_http.c
23  * @brief http 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 "gnunet_transport_plugin.h"
39 #include "gnunet_os_lib.h"
40 #include "gnunet_nat_lib.h"
41 #include "microhttpd.h"
42 #include <curl/curl.h>
43
44 #if BUILD_HTTPS
45 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_init
46 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_done
47 #define LIBGNUNET_PLUGIN_TRANSPORT_COMPONENT transport_https
48 #define PROTOCOL_PREFIX "https"
49 #else
50 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_init
51 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_done
52 #define LIBGNUNET_PLUGIN_TRANSPORT_COMPONENT transport_http
53 #define PROTOCOL_PREFIX "http"
54 #endif
55
56 #define DEBUG_HTTP GNUNET_NO
57 #define DEBUG_CURL GNUNET_NO
58 #define DEBUG_MHD GNUNET_NO
59 #define DEBUG_CONNECTIONS GNUNET_NO
60 #define DEBUG_SESSION_SELECTION GNUNET_NO
61 #define DEBUG_SCHEDULING GNUNET_NO
62 #define CURL_TCP_NODELAY GNUNET_YES
63
64 #define INBOUND GNUNET_NO
65 #define OUTBOUND GNUNET_YES
66
67
68
69 /**
70  * Text of the response sent back after the last bytes of a PUT
71  * request have been received (just to formally obey the HTTP
72  * protocol).
73  */
74 #define HTTP_PUT_RESPONSE "Thank you!"
75
76 /**
77  * After how long do we expire an address that we
78  * learned from another peer if it is not reconfirmed
79  * by anyone?
80  */
81 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
82
83 /**
84  * Page returned if request invalid
85  */
86 #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>"
87
88 /**
89  * Timeout for a http connect
90  */
91 #define HTTP_CONNECT_TIMEOUT 30
92
93 /**
94  * Network format for IPv4 addresses.
95  */
96 struct IPv4HttpAddress
97 {
98   /**
99    * IPv4 address, in network byte order.
100    */
101   uint32_t ipv4_addr GNUNET_PACKED;
102
103   /**
104    * Port number, in network byte order.
105    */
106   uint16_t port GNUNET_PACKED;
107 };
108
109 /**
110  * Wrapper to manage IPv4 addresses
111  */
112 struct IPv4HttpAddressWrapper
113 {
114   /**
115    * Linked list next
116    */
117   struct IPv4HttpAddressWrapper *next;
118
119   /**
120    * Linked list previous
121    */
122   struct IPv4HttpAddressWrapper *prev;
123
124   struct IPv4HttpAddress *addr;
125 };
126
127 /**
128  * Network format for IPv6 addresses.
129  */
130 struct IPv6HttpAddress
131 {
132   /**
133    * IPv6 address.
134    */
135   struct in6_addr ipv6_addr GNUNET_PACKED;
136
137   /**
138    * Port number, in network byte order.
139    */
140   uint16_t port GNUNET_PACKED;
141
142 };
143
144 /**
145  * Wrapper for IPv4 addresses.
146  */
147 struct IPv6HttpAddressWrapper
148 {
149   /**
150    * Linked list next
151    */
152   struct IPv6HttpAddressWrapper *next;
153
154   /**
155    * Linked list previous
156    */
157   struct IPv6HttpAddressWrapper *prev;
158
159   struct IPv6HttpAddress *addr;
160 };
161
162 /**
163  *  Message to send using http
164  */
165 struct HTTP_Message
166 {
167   /**
168    * next pointer for double linked list
169    */
170   struct HTTP_Message *next;
171
172   /**
173    * previous pointer for double linked list
174    */
175   struct HTTP_Message *prev;
176
177   /**
178    * buffer containing data to send
179    */
180   char *buf;
181
182   /**
183    * amount of data already sent
184    */
185   size_t pos;
186
187   /**
188    * buffer length
189    */
190   size_t size;
191
192   /**
193    * Continuation function to call once the transmission buffer
194    * has again space available.  NULL if there is no
195    * continuation to call.
196    */
197   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
198
199   /**
200    * Closure for transmit_cont.
201    */
202   void *transmit_cont_cls;
203 };
204
205
206 struct HTTP_PeerContext
207 {
208   /**
209    * peer's identity
210    */
211   struct GNUNET_PeerIdentity identity;
212
213   /**
214    * Pointer to the global plugin struct.
215    */
216   struct Plugin *plugin;
217
218   /**
219    * Linked list of connections with this peer
220    * head
221    */
222   struct Session *head;
223
224   /**
225    * Linked list of connections with this peer
226    * tail
227    */
228   struct Session *tail;
229
230   /**
231    * id for next session
232    */
233   size_t session_id_counter;
234
235   /**
236    * Last session used to send data
237    */
238   struct Session *last_session;
239
240   /**
241    * The task resetting inbound quota delay
242    */
243   GNUNET_SCHEDULER_TaskIdentifier reset_task;
244
245   /**
246    * Delay from transport service inbound quota tracker when to receive data again
247    */
248   struct GNUNET_TIME_Relative delay;
249 };
250
251
252 struct Session
253 {
254   /**
255    * API requirement.
256    */
257   struct SessionHeader header;
258
259   /**
260    * next session in linked list
261    */
262   struct Session *next;
263
264   /**
265    * previous session in linked list
266    */
267   struct Session *prev;
268
269   /**
270    * address of this session
271    */
272   void *addr;
273
274   /**
275    * address length
276    */
277   size_t addrlen;
278
279   /**
280    * target url
281    */
282   char *url;
283
284   /**
285    * Message queue for outbound messages
286    * head of queue
287    */
288   struct HTTP_Message *pending_msgs_head;
289
290   /**
291    * Message queue for outbound messages
292    * tail of queue
293    */
294   struct HTTP_Message *pending_msgs_tail;
295
296   /**
297    * partner peer this connection belongs to
298    */
299   struct HTTP_PeerContext *peercontext;
300
301   /**
302    * message stream tokenizer for incoming data
303    */
304   struct GNUNET_SERVER_MessageStreamTokenizer *msgtok;
305
306   /**
307    * session direction
308    * outbound: OUTBOUND (GNUNET_YES)
309    * inbound : INBOUND (GNUNET_NO)
310    */
311   unsigned int direction;
312
313   /**
314    * is session connected to send data?
315    */
316   unsigned int send_connected;
317
318   /**
319    * is send connection active?
320    */
321   unsigned int send_active;
322
323   /**
324    * connection disconnect forced (e.g. from transport)
325    */
326   unsigned int send_force_disconnect;
327
328   /**
329    * is session connected to receive data?
330    */
331   unsigned int recv_connected;
332
333   /**
334    * is receive connection active?
335    */
336   unsigned int recv_active;
337
338   /**
339    * connection disconnect forced (e.g. from transport)
340    */
341   unsigned int recv_force_disconnect;
342
343
344   /**
345    * id for next session
346    * NOTE: 0 is not an ID, zero is not defined. A correct ID is always > 0
347    */
348   size_t session_id;
349
350   /**
351    * entity managing sending data
352    * outbound session: CURL *
353    * inbound session: mhd_connection *
354    */
355   void *send_endpoint;
356
357   /**
358    * entity managing recieving data
359    * outbound session: CURL *
360    * inbound session: mhd_connection *
361    */
362   void *recv_endpoint;
363
364   /**
365    * Current queue size
366    */
367   size_t queue_length_cur;
368
369   /**
370         * Max queue size
371         */
372   size_t queue_length_max;
373
374 };
375
376 /**
377  * Encapsulation of all of the state of the plugin.
378  */
379 struct Plugin
380 {
381   /**
382    * Our environment.
383    */
384   struct GNUNET_TRANSPORT_PluginEnvironment *env;
385
386   /**
387    * Handle for reporting statistics.
388    */
389   struct GNUNET_STATISTICS_Handle *stats;
390
391   /**
392    * Plugin Port
393    */
394   uint16_t port_inbound;
395
396   struct GNUNET_CONTAINER_MultiHashMap *peers;
397
398   /**
399    * Daemon for listening for new IPv4 connections.
400    */
401   struct MHD_Daemon *http_server_daemon_v4;
402
403   /**
404    * Daemon for listening for new IPv6connections.
405    */
406   struct MHD_Daemon *http_server_daemon_v6;
407
408   /**
409    * Our primary task for http daemon handling IPv4 connections
410    */
411   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v4;
412
413   /**
414    * Our primary task for http daemon handling IPv6 connections
415    */
416   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v6;
417
418   /**
419    * The task sending data
420    */
421   GNUNET_SCHEDULER_TaskIdentifier http_curl_task;
422
423   /**
424    * cURL Multihandle
425    */
426   CURLM *multi_handle;
427
428   /**
429    * Our handle to the NAT module.
430    */
431   struct GNUNET_NAT_Handle *nat;
432
433   /**
434    * ipv4 DLL head
435    */
436   struct IPv4HttpAddressWrapper *ipv4_addr_head;
437
438   /**
439    * ipv4 DLL tail
440    */
441   struct IPv4HttpAddressWrapper *ipv4_addr_tail;
442
443   /**
444    * ipv6 DLL head
445    */
446   struct IPv6HttpAddressWrapper *ipv6_addr_head;
447
448   /**
449    * ipv6 DLL tail
450    */
451   struct IPv6HttpAddressWrapper *ipv6_addr_tail;
452
453   /**
454    * Our ASCII encoded, hashed peer identity
455    * This string is used to distinguish between connections and is added to the urls
456    */
457   struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
458
459   /**
460    * IPv4 Address the plugin binds to
461    */
462   struct sockaddr_in *bind4_address;
463
464   /**
465    * IPv6 Address the plugins binds to
466    */
467   struct sockaddr_in6 *bind6_address;
468
469   /**
470    * Hostname to bind to
471    */
472   char *bind_hostname;
473
474   /**
475    * Is IPv4 enabled?
476    */
477   int use_ipv6;
478
479   /**
480    * Is IPv6 enabled?
481    */
482   int use_ipv4;
483
484   /**
485    * use local addresses?
486    */
487   int use_localaddresses;
488
489   /**
490    * maximum number of connections
491    */
492   int max_connect_per_transport;
493
494   /**
495    * Current number of connections;
496    */
497   int current_connections;
498
499   /**
500    * Closure passed by MHD to the mhd_logger function
501    */
502   void *mhd_log;
503
504   /* only needed for HTTPS plugin */
505 #if BUILD_HTTPS
506   /* The certificate MHD uses as an \0 terminated string */
507   char *cert;
508
509   /* The private key MHD uses as an \0 terminated string */
510   char *key;
511
512   /* crypto init string */
513   char *crypto_init;
514 #endif
515 };
516
517 /**
518  * Context for address to string conversion.
519  */
520 struct PrettyPrinterContext
521 {
522   /**
523    * Function to call with the result.
524    */
525   GNUNET_TRANSPORT_AddressStringCallback asc;
526
527   /**
528    * Clsoure for 'asc'.
529    */
530   void *asc_cls;
531
532   /**
533    * Port to add after the IP address.
534    */
535   uint16_t port;
536 };
537
538
539 /**
540  * Function called for a quick conversion of the binary address to
541  * a numeric address.  Note that the caller must not free the
542  * address and that the next call to this function is allowed
543  * to override the address again.
544  *
545  * @param cls closure
546  * @param addr binary address
547  * @param addrlen length of the address
548  * @return string representing the same address
549  */
550 static const char *
551 http_plugin_address_to_string (void *cls, const void *addr, size_t addrlen);
552
553
554 /**
555  * Call MHD to process pending ipv4 requests and then go back
556  * and schedule the next run.
557  */
558 static void
559 http_server_daemon_v4_run (void *cls,
560                            const struct GNUNET_SCHEDULER_TaskContext *tc);
561 /**
562  * Call MHD to process pending ipv6 requests and then go back
563  * and schedule the next run.
564  */
565 static void
566 http_server_daemon_v6_run (void *cls,
567                            const struct GNUNET_SCHEDULER_TaskContext *tc);
568
569 /**
570  * Function setting up curl handle and selecting message to send
571  *
572  * @param plugin plugin
573  * @param ps session
574  * @return GNUNET_SYSERR on failure, GNUNET_NO if connecting, GNUNET_YES if ok
575  */
576 static int
577 send_check_connections (struct Plugin *plugin, struct Session *ps);
578
579 /**
580  * Function setting up file descriptors and scheduling task to run
581  *
582  * @param  plugin plugin as closure
583  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
584  */
585 static int
586 curl_schedule (struct Plugin *plugin);
587
588 /**
589  * Task scheduled to reset the inbound quota delay for a specific peer
590  * @param cls plugin as closure
591  * @param tc task context
592  */
593 static void
594 reset_inbound_quota_delay (void *cls,
595                            const struct GNUNET_SCHEDULER_TaskContext *tc)
596 {
597   struct HTTP_PeerContext *pc = cls;
598
599   GNUNET_assert (cls != NULL);
600   pc->reset_task = GNUNET_SCHEDULER_NO_TASK;
601   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
602     return;
603   pc->delay = GNUNET_TIME_relative_get_zero ();
604 }
605
606
607 /**
608  * Creates a valid url from passed address and id
609  * @param plugin plugin
610  * @param addr address to create url from
611  * @param addrlen address lenth
612  * @param id session id
613  * @return the created url
614  */
615 static char *
616 create_url (struct Plugin *plugin, const void *addr, size_t addrlen, size_t id)
617 {
618   char *url = NULL;
619   char *addr_str = (char *) http_plugin_address_to_string (NULL, addr, addrlen);
620
621   GNUNET_assert ((addr != NULL) && (addrlen != 0));
622   GNUNET_asprintf (&url, "%s://%s/%s;%u", PROTOCOL_PREFIX, addr_str,
623                    (char *) (&plugin->my_ascii_hash_ident), id);
624   return url;
625 }
626
627
628 /**
629  * Removes a message from the linked list of messages
630  * @param ps session
631  * @param msg message
632  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
633  */
634 static int
635 remove_http_message (struct Session *ps, struct HTTP_Message *msg)
636 {
637   GNUNET_CONTAINER_DLL_remove (ps->pending_msgs_head, ps->pending_msgs_tail,
638                                msg);
639   GNUNET_free (msg);
640   return GNUNET_OK;
641 }
642
643 /**
644  * Iterator to remove peer context
645  * @param cls the plugin
646  * @param key the peers public key hashcode
647  * @param value the peer context
648  * @return GNUNET_YES on success
649  */
650 static int
651 remove_peer_context_Iterator (void *cls, const GNUNET_HashCode * key,
652                               void *value)
653 {
654   struct Plugin *plugin = cls;
655   struct HTTP_PeerContext *pc = value;
656   struct Session *ps = pc->head;
657   struct Session *tmp = NULL;
658   struct HTTP_Message *msg = NULL;
659   struct HTTP_Message *msg_tmp = NULL;
660
661 #if DEBUG_HTTP
662   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Freeing context for peer `%s'\n",
663               GNUNET_i2s (&pc->identity));
664 #endif
665   GNUNET_assert (GNUNET_YES ==
666                  GNUNET_CONTAINER_multihashmap_remove (plugin->peers,
667                                                        &pc->identity.hashPubKey,
668                                                        pc));
669   while (ps != NULL)
670   {
671     plugin->env->session_end (plugin, &pc->identity, ps);
672     tmp = ps->next;
673
674     GNUNET_free_non_null (ps->addr);
675     GNUNET_free (ps->url);
676     if (ps->msgtok != NULL)
677       GNUNET_SERVER_mst_destroy (ps->msgtok);
678
679     msg = ps->pending_msgs_head;
680     while (msg != NULL)
681     {
682       msg_tmp = msg->next;
683       GNUNET_free (msg);
684       msg = msg_tmp;
685     }
686     if (ps->direction == OUTBOUND)
687     {
688       if (ps->send_endpoint != NULL)
689         curl_easy_cleanup (ps->send_endpoint);
690       if (ps->recv_endpoint != NULL)
691         curl_easy_cleanup (ps->recv_endpoint);
692     }
693     GNUNET_free (ps);
694     ps = tmp;
695   }
696   GNUNET_free (pc);
697   GNUNET_STATISTICS_update (plugin->env->stats,
698                             gettext_noop ("# HTTP peers active"), -1,
699                             GNUNET_NO);
700   return GNUNET_YES;
701 }
702
703
704 /**
705  * Removes a session from the linked list of sessions
706  * @param pc peer context
707  * @param ps session
708  * @param call_msg_cont GNUNET_YES to call pending message continuations, otherwise no
709  * @param call_msg_cont_result result to call message continuations with
710  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
711  */
712 static int
713 remove_session (struct HTTP_PeerContext *pc, struct Session *ps,
714                 int call_msg_cont, int call_msg_cont_result)
715 {
716   struct HTTP_Message *msg;
717   struct Plugin *plugin = ps->peercontext->plugin;
718
719 #if DEBUG_CONNECTIONS
720   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
721               "Connection %X: removing %s session %X with id %u\n", ps,
722               (ps->direction == INBOUND) ? "inbound" : "outbound", ps,
723               ps->session_id);
724 #endif
725   plugin->env->session_end (plugin, &pc->identity, ps);
726   GNUNET_free_non_null (ps->addr);
727   GNUNET_SERVER_mst_destroy (ps->msgtok);
728   GNUNET_free (ps->url);
729   if (ps->direction == INBOUND)
730   {
731     if (ps->recv_endpoint != NULL)
732     {
733       curl_easy_cleanup (ps->recv_endpoint);
734       ps->recv_endpoint = NULL;
735     }
736     if (ps->send_endpoint != NULL)
737     {
738       curl_easy_cleanup (ps->send_endpoint);
739       ps->send_endpoint = NULL;
740     }
741   }
742
743   msg = ps->pending_msgs_head;
744   while (msg != NULL)
745   {
746     if ((call_msg_cont == GNUNET_YES) && (msg->transmit_cont != NULL))
747     {
748       msg->transmit_cont (msg->transmit_cont_cls, &pc->identity,
749                           call_msg_cont_result);
750     }
751     GNUNET_CONTAINER_DLL_remove (ps->pending_msgs_head, ps->pending_msgs_head,
752                                  msg);
753     GNUNET_free (msg);
754     msg = ps->pending_msgs_head;
755   }
756
757   GNUNET_CONTAINER_DLL_remove (pc->head, pc->tail, ps);
758   GNUNET_free (ps);
759   ps = NULL;
760
761   /* no sessions left remove peer */
762   if (pc->head == NULL)
763   {
764 #if DEBUG_HTTP
765     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
766                 "No sessions left for peer `%s', removing context\n",
767                 GNUNET_i2s (&pc->identity));
768 #endif
769     remove_peer_context_Iterator (plugin, &pc->identity.hashPubKey, pc);
770   }
771
772   return GNUNET_OK;
773 }
774
775
776 #if 0
777 static int
778 check_localaddress (const struct sockaddr *addr, socklen_t addrlen)
779 {
780   uint32_t res = 0;
781   int local = GNUNET_NO;
782   int af = addr->sa_family;
783
784   switch (af)
785   {
786   case AF_INET:
787   {
788     uint32_t netmask = 0x7F000000;
789     uint32_t address = ntohl (((struct sockaddr_in *) addr)->sin_addr.s_addr);
790
791     res = (address >> 24) ^ (netmask >> 24);
792     if (res != 0)
793       local = GNUNET_NO;
794     else
795       local = GNUNET_YES;
796 #if DEBUG_HTTP
797     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking IPv4 address `%s': %s\n",
798                 GNUNET_a2s (addr, addrlen),
799                 (local == GNUNET_YES) ? "local" : "global");
800 #endif
801     break;
802   }
803   case AF_INET6:
804   {
805     if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr) ||
806         IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
807       local = GNUNET_YES;
808     else
809       local = GNUNET_NO;
810 #if DEBUG_HTTP
811     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking IPv6 address `%s' : %s\n",
812                 GNUNET_a2s (addr, addrlen),
813                 (local == GNUNET_YES) ? "local" : "global");
814 #endif
815     break;
816   }
817   }
818   return local;
819 }
820
821
822 /**
823  * Add the IP of our network interface to the list of
824  * our external IP addresses.
825  *
826  * @param cls the 'struct Plugin*'
827  * @param name name of the interface
828  * @param isDefault do we think this may be our default interface
829  * @param addr address of the interface
830  * @param addrlen number of bytes in addr
831  * @return GNUNET_OK to continue iterating
832  */
833 static int
834 process_interfaces (void *cls, const char *name, int isDefault,
835                     const struct sockaddr *addr, socklen_t addrlen)
836 {
837   struct Plugin *plugin = cls;
838   struct IPv4HttpAddress *t4;
839   struct IPv6HttpAddress *t6;
840   int af;
841
842   if (plugin->use_localaddresses == GNUNET_NO)
843   {
844     if (GNUNET_YES == check_localaddress (addr, addrlen))
845     {
846 #if DEBUG_HTTP
847       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, PROTOCOL_PREFIX,
848                        "Not notifying transport of address `%s' (local address)\n",
849                        GNUNET_a2s (addr, addrlen));
850 #endif
851       return GNUNET_OK;
852     }
853   }
854
855
856   GNUNET_assert (cls != NULL);
857   af = addr->sa_family;
858   if ((af == AF_INET) && (plugin->use_ipv4 == GNUNET_YES) &&
859       (plugin->bind6_address == NULL))
860   {
861
862     struct in_addr bnd_cmp = ((struct sockaddr_in *) addr)->sin_addr;
863
864     t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddress));
865     // Not skipping loopback addresses
866
867
868     t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
869     t4->port = htons (plugin->port_inbound);
870     if (plugin->bind4_address != NULL)
871     {
872       if (0 ==
873           memcmp (&plugin->bind4_address->sin_addr, &bnd_cmp,
874                   sizeof (struct in_addr)))
875       {
876         GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head,
877                                      plugin->ipv4_addr_tail, t4);
878         plugin->env->notify_address (plugin->env->cls, GNUNET_YES, t4,
879                                      sizeof (struct IPv4HttpAddress));
880         return GNUNET_OK;
881       }
882       GNUNET_free (t4);
883       return GNUNET_OK;
884     }
885     else
886     {
887       GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head,
888                                    plugin->ipv4_addr_tail, t4);
889       plugin->env->notify_address (plugin->env->cls, GNUNET_YES, t4,
890                                    sizeof (struct IPv4HttpAddress));
891       return GNUNET_OK;
892     }
893   }
894   if ((af == AF_INET6) && (plugin->use_ipv6 == GNUNET_YES) &&
895       (plugin->bind4_address == NULL))
896   {
897
898     struct in6_addr bnd_cmp6 = ((struct sockaddr_in6 *) addr)->sin6_addr;
899
900     t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddress));
901     GNUNET_assert (t6 != NULL);
902
903     if (plugin->bind6_address != NULL)
904     {
905       if (0 ==
906           memcmp (&plugin->bind6_address->sin6_addr, &bnd_cmp6,
907                   sizeof (struct in6_addr)))
908       {
909         memcpy (&t6->ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
910                 sizeof (struct in6_addr));
911         t6->port = htons (plugin->port_inbound);
912         plugin->env->notify_address (plugin->env->cls, GNUNET_YES, t6,
913                                      sizeof (struct IPv6HttpAddress));
914         GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head,
915                                      plugin->ipv6_addr_tail, t6);
916         return GNUNET_OK;
917       }
918       GNUNET_free (t6);
919       return GNUNET_OK;
920     }
921     memcpy (&t6->ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
922             sizeof (struct in6_addr));
923     t6->port = htons (plugin->port_inbound);
924     GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head, plugin->ipv6_addr_tail,
925                                  t6);
926
927     plugin->env->notify_address (plugin->env->cls, GNUNET_YES, t6,
928                                  sizeof (struct IPv6HttpAddress));
929   }
930   return GNUNET_OK;
931 }
932 #endif
933
934 /**
935  * External logging function for MHD
936  * @param arg arguments
937  * @param fmt format string
938  * @param ap  list of arguments
939  */
940 static void
941 mhd_logger (void *arg, const char *fmt, va_list ap)
942 {
943   char text[1024];
944
945   vsnprintf (text, sizeof (text), fmt, ap);
946   va_end (ap);
947   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MHD: %s\n", text);
948 }
949
950
951 static void
952 mhd_termination_cb (void *cls, struct MHD_Connection *connection,
953                     void **httpSessionCache)
954 {
955   struct Session *ps = *httpSessionCache;
956
957   if (ps == NULL)
958     return;
959   struct HTTP_PeerContext *pc = ps->peercontext;
960   struct Plugin *plugin = cls;
961
962   GNUNET_assert (cls != NULL);
963   plugin->current_connections--;
964
965   if (connection == ps->recv_endpoint)
966   {
967 #if DEBUG_CONNECTIONS
968     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
969                 "Connection %X: inbound connection from peer `%s' was terminated\n",
970                 ps, GNUNET_i2s (&pc->identity));
971 #endif
972     ps->recv_active = GNUNET_NO;
973     ps->recv_connected = GNUNET_NO;
974     ps->recv_endpoint = NULL;
975   }
976   if (connection == ps->send_endpoint)
977   {
978     ps->send_active = GNUNET_NO;
979     ps->send_connected = GNUNET_NO;
980     ps->send_endpoint = NULL;
981 #if DEBUG_CONNECTIONS
982     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
983                 "Connection %X: outbound connection from peer `%s' was terminated\n",
984                 ps, GNUNET_i2s (&pc->identity));
985 #endif
986   }
987
988   /* if both connections disconnected, remove session */
989   if ((ps->send_connected == GNUNET_NO) && (ps->recv_connected == GNUNET_NO))
990   {
991     GNUNET_STATISTICS_update (pc->plugin->env->stats,
992                               gettext_noop
993                               ("# HTTP inbound sessions for peers active"), -1,
994                               GNUNET_NO);
995     remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR);
996   }
997 }
998
999
1000 /**
1001  * Callback called by MessageStreamTokenizer when a message has arrived
1002  * @param cls current session as closure
1003  * @param client clien
1004  * @param message the message to be forwarded to transport service
1005  */
1006 static void
1007 mhd_write_mst_cb (void *cls, void *client,
1008                   const struct GNUNET_MessageHeader *message)
1009 {
1010   struct Session *ps = cls;
1011   struct HTTP_PeerContext *pc;
1012   struct GNUNET_TIME_Relative delay;
1013
1014   GNUNET_assert (ps != NULL);
1015   pc = ps->peercontext;
1016   GNUNET_assert (pc != NULL);
1017 #if DEBUG_HTTP
1018   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1019               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
1020               ps, ntohs (message->type), ntohs (message->size),
1021               GNUNET_i2s (&(ps->peercontext)->identity),
1022               http_plugin_address_to_string (NULL, ps->addr, ps->addrlen));
1023 #endif
1024   struct GNUNET_TRANSPORT_ATS_Information distance[2];
1025
1026   distance[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
1027   distance[0].value = htonl (1);
1028   distance[1].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
1029   distance[1].value = htonl (0);
1030   delay =
1031       pc->plugin->env->receive (ps->peercontext->plugin->env->cls,
1032                                 &pc->identity, message,
1033                                 (const struct GNUNET_TRANSPORT_ATS_Information
1034                                  *) &distance, 2, ps, NULL, 0);
1035   pc->delay = delay;
1036   if (pc->reset_task != GNUNET_SCHEDULER_NO_TASK)
1037     GNUNET_SCHEDULER_cancel (pc->reset_task);
1038
1039   if (delay.rel_value > 0)
1040   {
1041 #if DEBUG_HTTP
1042     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1043                 "Connection %X: Inbound quota management: delay next read for %llu ms \n",
1044                 ps, delay.rel_value);
1045 #endif
1046     pc->reset_task =
1047         GNUNET_SCHEDULER_add_delayed (delay, &reset_inbound_quota_delay, pc);
1048   }
1049 }
1050
1051
1052 /**
1053  * Check if incoming connection is accepted.
1054  * NOTE: Here every connection is accepted
1055  * @param cls plugin as closure
1056  * @param addr address of incoming connection
1057  * @param addr_len address length of incoming connection
1058  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
1059  *
1060  */
1061 static int
1062 mhd_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
1063 {
1064   struct Plugin *plugin = cls;
1065
1066   GNUNET_assert (cls != NULL);
1067
1068   if (plugin->max_connect_per_transport > plugin->current_connections)
1069   {
1070     plugin->current_connections++;
1071     return MHD_YES;
1072   }
1073   else
1074     return MHD_NO;
1075 }
1076
1077
1078 /**
1079  * Callback called by MHD when it needs data to send
1080  * @param cls current session
1081  * @param pos position in buffer
1082  * @param buf the buffer to write data to
1083  * @param max max number of bytes available in buffer
1084  * @return bytes written to buffer
1085  */
1086 static ssize_t
1087 mhd_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
1088 {
1089   struct Session *ps = cls;
1090   struct HTTP_PeerContext *pc;
1091   struct HTTP_Message *msg;
1092   int bytes_read = 0;
1093
1094   GNUNET_assert (ps != NULL);
1095
1096   pc = ps->peercontext;
1097   msg = ps->pending_msgs_tail;
1098   if (ps->send_force_disconnect == GNUNET_YES)
1099   {
1100 #if DEBUG_CONNECTIONS
1101     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1102                 "Connection %X: outbound forced to disconnect\n", ps);
1103 #endif
1104     return -1;
1105   }
1106
1107   if (msg != NULL)
1108   {
1109     /* sending */
1110     if ((msg->size - msg->pos) <= max)
1111     {
1112       memcpy (buf, &msg->buf[msg->pos], (msg->size - msg->pos));
1113       bytes_read = msg->size - msg->pos;
1114       msg->pos += (msg->size - msg->pos);
1115     }
1116     else
1117     {
1118       memcpy (buf, &msg->buf[msg->pos], max);
1119       msg->pos += max;
1120       bytes_read = max;
1121     }
1122
1123     /* removing message */
1124     if (msg->pos == msg->size)
1125     {
1126       if (NULL != msg->transmit_cont)
1127         msg->transmit_cont (msg->transmit_cont_cls, &pc->identity, GNUNET_OK);
1128       ps->queue_length_cur -= msg->size;
1129       remove_http_message (ps, msg);
1130     }
1131   }
1132 #if DEBUG_CONNECTIONS
1133   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection %X: MHD has sent %u bytes\n",
1134               ps, bytes_read);
1135 #endif
1136   return bytes_read;
1137 }
1138
1139
1140 /**
1141  * Process GET or PUT request received via MHD.  For
1142  * GET, queue response that will send back our pending
1143  * messages.  For PUT, process incoming data and send
1144  * to GNUnet core.  In either case, check if a session
1145  * already exists and create a new one if not.
1146  */
1147 static int
1148 mhd_access_cb (void *cls, struct MHD_Connection *mhd_connection,
1149                const char *url, const char *method, const char *version,
1150                const char *upload_data, size_t * upload_data_size,
1151                void **httpSessionCache)
1152 {
1153   struct Plugin *plugin = cls;
1154   struct MHD_Response *response;
1155   const union MHD_ConnectionInfo *conn_info;
1156   const struct sockaddr *client_addr;
1157   const struct sockaddr_in *addrin;
1158   const struct sockaddr_in6 *addrin6;
1159   char address[INET6_ADDRSTRLEN + 14];
1160   struct GNUNET_PeerIdentity pi_in;
1161   size_t id_num = 0;
1162   struct IPv4HttpAddress ipv4addr;
1163   struct IPv6HttpAddress ipv6addr;
1164   struct HTTP_PeerContext *pc = NULL;
1165   struct Session *ps = NULL;
1166   struct Session *ps_tmp = NULL;
1167   int res = GNUNET_NO;
1168   void *addr = NULL;
1169   size_t addr_len = 0;
1170
1171   GNUNET_assert (cls != NULL);
1172
1173   if (NULL == *httpSessionCache)
1174   {
1175     /* check url for peer identity , if invalid send HTTP 404 */
1176     size_t len = strlen (&url[1]);
1177     char *peer = GNUNET_malloc (104 + 1);
1178
1179     if ((len > 104) && (url[104] == ';'))
1180     {
1181       char *id = GNUNET_malloc ((len - 104) + 1);
1182
1183       strcpy (id, &url[105]);
1184       memcpy (peer, &url[1], 103);
1185       peer[103] = '\0';
1186       id_num = strtoul (id, NULL, 10);
1187       GNUNET_free (id);
1188     }
1189     res = GNUNET_CRYPTO_hash_from_string (peer, &(pi_in.hashPubKey));
1190     GNUNET_free (peer);
1191     if (GNUNET_SYSERR == res)
1192     {
1193       response =
1194           MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),
1195                                          HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
1196       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
1197       MHD_destroy_response (response);
1198 #if DEBUG_CONNECTIONS
1199       if (res == MHD_YES)
1200         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1201                     "Peer has no valid ident, sent HTTP 1.1/404\n");
1202       else
1203         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1204                     "Peer has no valid ident, could not send error\n");
1205 #endif
1206       return res;
1207     }
1208   }
1209   else
1210   {
1211     ps = *httpSessionCache;
1212     pc = ps->peercontext;
1213   }
1214
1215   if (NULL == *httpSessionCache)
1216   {
1217     /* get peer context */
1218     pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &pi_in.hashPubKey);
1219     /* Peer unknown */
1220     if (pc == NULL)
1221     {
1222       pc = GNUNET_malloc (sizeof (struct HTTP_PeerContext));
1223       pc->plugin = plugin;
1224       pc->session_id_counter = 1;
1225       pc->last_session = NULL;
1226       memcpy (&pc->identity, &pi_in, sizeof (struct GNUNET_PeerIdentity));
1227       GNUNET_CONTAINER_multihashmap_put (plugin->peers,
1228                                          &pc->identity.hashPubKey, pc,
1229                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1230       GNUNET_STATISTICS_update (plugin->env->stats,
1231                                 gettext_noop ("# HTTP peers active"), 1,
1232                                 GNUNET_NO);
1233     }
1234
1235     conn_info =
1236         MHD_get_connection_info (mhd_connection,
1237                                  MHD_CONNECTION_INFO_CLIENT_ADDRESS);
1238     /* Incoming IPv4 connection */
1239     /* cast required for legacy MHD API < 0.9.6 */
1240     client_addr = (const struct sockaddr *) conn_info->client_addr;
1241     if (AF_INET == client_addr->sa_family)
1242     {
1243       addrin = (const struct sockaddr_in *) client_addr;
1244       inet_ntop (addrin->sin_family, &(addrin->sin_addr), address,
1245                  INET_ADDRSTRLEN);
1246       memcpy (&ipv4addr.ipv4_addr, &(addrin->sin_addr),
1247               sizeof (struct in_addr));
1248       ipv4addr.port = addrin->sin_port;
1249       addr = &ipv4addr;
1250       addr_len = sizeof (struct IPv4HttpAddress);
1251     }
1252     /* Incoming IPv6 connection */
1253     if (AF_INET6 == client_addr->sa_family)
1254     {
1255       addrin6 = (const struct sockaddr_in6 *) client_addr;
1256       inet_ntop (addrin6->sin6_family, &(addrin6->sin6_addr), address,
1257                  INET6_ADDRSTRLEN);
1258       memcpy (&ipv6addr.ipv6_addr, &(addrin6->sin6_addr),
1259               sizeof (struct in6_addr));
1260       ipv6addr.port = addrin6->sin6_port;
1261       addr = &ipv6addr;
1262       addr_len = sizeof (struct IPv6HttpAddress);
1263     }
1264
1265     GNUNET_assert (addr != NULL);
1266     GNUNET_assert (addr_len != 0);
1267
1268     ps = NULL;
1269     /* only inbound sessions here */
1270
1271     ps_tmp = pc->head;
1272     while (ps_tmp != NULL)
1273     {
1274       if ((ps_tmp->direction == INBOUND) && (ps_tmp->session_id == id_num) &&
1275           (id_num != 0))
1276       {
1277         if ((ps_tmp->recv_force_disconnect != GNUNET_YES) &&
1278             (ps_tmp->send_force_disconnect != GNUNET_YES))
1279           ps = ps_tmp;
1280         break;
1281       }
1282       ps_tmp = ps_tmp->next;
1283     }
1284
1285     if (ps == NULL)
1286     {
1287       ps = GNUNET_malloc (sizeof (struct Session));
1288       ps->addr = GNUNET_malloc (addr_len);
1289       memcpy (ps->addr, addr, addr_len);
1290       ps->addrlen = addr_len;
1291       ps->direction = INBOUND;
1292       ps->pending_msgs_head = NULL;
1293       ps->pending_msgs_tail = NULL;
1294       ps->send_connected = GNUNET_NO;
1295       ps->send_active = GNUNET_NO;
1296       ps->recv_connected = GNUNET_NO;
1297       ps->recv_active = GNUNET_NO;
1298       ps->peercontext = pc;
1299       ps->session_id = id_num;
1300       ps->queue_length_cur = 0;
1301       ps->queue_length_max = GNUNET_SERVER_MAX_MESSAGE_SIZE;
1302       ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
1303       GNUNET_CONTAINER_DLL_insert (pc->head, pc->tail, ps);
1304       GNUNET_STATISTICS_update (plugin->env->stats,
1305                                 gettext_noop
1306                                 ("# HTTP inbound sessions for peers active"), 1,
1307                                 GNUNET_NO);
1308     }
1309
1310     *httpSessionCache = ps;
1311     if (ps->msgtok == NULL)
1312       ps->msgtok = GNUNET_SERVER_mst_create (&mhd_write_mst_cb, ps);
1313 #if DEBUG_HTTP
1314     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1315                 "Connection %X: HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
1316                 ps, method, GNUNET_i2s (&pc->identity),
1317                 http_plugin_address_to_string (NULL, ps->addr, ps->addrlen));
1318 #endif
1319   }
1320
1321   /* Is it a PUT or a GET request */
1322   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
1323   {
1324     if (ps->recv_force_disconnect == GNUNET_YES)
1325     {
1326 #if DEBUG_CONNECTIONS
1327       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1328                   "Connection %X: inbound connection was forced to disconnect\n",
1329                   ps);
1330 #endif
1331       ps->recv_active = GNUNET_NO;
1332       return MHD_NO;
1333     }
1334     if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_NO))
1335     {
1336       ps->recv_endpoint = mhd_connection;
1337       ps->recv_connected = GNUNET_YES;
1338       ps->recv_active = GNUNET_YES;
1339       ps->recv_force_disconnect = GNUNET_NO;
1340 #if DEBUG_CONNECTIONS
1341       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1342                   "Connection %X: inbound PUT connection connected\n", ps);
1343 #endif
1344       return MHD_YES;
1345     }
1346
1347     /* Transmission of all data complete */
1348     if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_YES))
1349     {
1350       response =
1351           MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),
1352                                          HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
1353       res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1354 #if DEBUG_CONNECTIONS
1355       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1356                   "Connection %X: Sent HTTP/1.1: 200 OK as PUT Response\n", ps);
1357 #endif
1358       MHD_destroy_response (response);
1359       ps->recv_active = GNUNET_NO;
1360       return MHD_YES;
1361     }
1362
1363     /* Recieving data */
1364     if ((*upload_data_size > 0) && (ps->recv_active == GNUNET_YES))
1365     {
1366       if (pc->delay.rel_value == 0)
1367       {
1368 #if DEBUG_HTTP
1369         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1370                     "Connection %X: PUT with %u bytes forwarded to MST\n", ps,
1371                     *upload_data_size);
1372 #endif
1373         res =
1374             GNUNET_SERVER_mst_receive (ps->msgtok, ps, upload_data,
1375                                        *upload_data_size, GNUNET_NO, GNUNET_NO);
1376         (*upload_data_size) = 0;
1377       }
1378       else
1379       {
1380 #if DEBUG_HTTP
1381         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1382                     "Connection %X: no inbound bandwidth available! Next read was delayed for  %llu ms\n",
1383                     ps, ps->peercontext->delay.rel_value);
1384 #endif
1385       }
1386       return MHD_YES;
1387     }
1388     else
1389       return MHD_NO;
1390   }
1391   if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
1392   {
1393     if (ps->send_force_disconnect == GNUNET_YES)
1394     {
1395 #if DEBUG_CONNECTIONS
1396       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1397                   "Connection %X: outbound connection was  forced to disconnect\n",
1398                   ps);
1399 #endif
1400       ps->send_active = GNUNET_NO;
1401       return MHD_NO;
1402     }
1403     ps->send_connected = GNUNET_YES;
1404     ps->send_active = GNUNET_YES;
1405     ps->send_endpoint = mhd_connection;
1406     ps->send_force_disconnect = GNUNET_NO;
1407 #if DEBUG_CONNECTIONS
1408     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1409                 "Connection %X: inbound GET connection connected\n", ps);
1410 #endif
1411     response =
1412         MHD_create_response_from_callback (-1, 32 * 1024, &mhd_send_callback,
1413                                            ps, NULL);
1414     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1415     MHD_destroy_response (response);
1416     return MHD_YES;
1417   }
1418   return MHD_NO;
1419 }
1420
1421
1422 /**
1423  * Function that queries MHD's select sets and
1424  * starts the task waiting for them.
1425  * @param plugin plugin
1426  * @param daemon_handle the MHD daemon handle
1427  * @return gnunet task identifier
1428  */
1429 static GNUNET_SCHEDULER_TaskIdentifier
1430 http_server_daemon_prepare (struct Plugin *plugin,
1431                             struct MHD_Daemon *daemon_handle)
1432 {
1433   GNUNET_SCHEDULER_TaskIdentifier ret;
1434   fd_set rs;
1435   fd_set ws;
1436   fd_set es;
1437   struct GNUNET_NETWORK_FDSet *wrs;
1438   struct GNUNET_NETWORK_FDSet *wws;
1439   struct GNUNET_NETWORK_FDSet *wes;
1440   int max;
1441   unsigned long long timeout;
1442   int haveto;
1443   struct GNUNET_TIME_Relative tv;
1444
1445   ret = GNUNET_SCHEDULER_NO_TASK;
1446   FD_ZERO (&rs);
1447   FD_ZERO (&ws);
1448   FD_ZERO (&es);
1449   wrs = GNUNET_NETWORK_fdset_create ();
1450   wes = GNUNET_NETWORK_fdset_create ();
1451   wws = GNUNET_NETWORK_fdset_create ();
1452   max = -1;
1453   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
1454   haveto = MHD_get_timeout (daemon_handle, &timeout);
1455   if (haveto == MHD_YES)
1456     tv.rel_value = (uint64_t) timeout;
1457   else
1458     tv = GNUNET_TIME_UNIT_SECONDS;
1459   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1460   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1461   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1462   if (daemon_handle == plugin->http_server_daemon_v4)
1463   {
1464     if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1465     {
1466       GNUNET_SCHEDULER_cancel (plugin->http_server_task_v4);
1467       plugin->http_server_daemon_v4 = GNUNET_SCHEDULER_NO_TASK;
1468     }
1469
1470     ret =
1471         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1472                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
1473                                      &http_server_daemon_v4_run, plugin);
1474   }
1475   if (daemon_handle == plugin->http_server_daemon_v6)
1476   {
1477     if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1478     {
1479       GNUNET_SCHEDULER_cancel (plugin->http_server_task_v6);
1480       plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1481     }
1482
1483     ret =
1484         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1485                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
1486                                      &http_server_daemon_v6_run, plugin);
1487   }
1488   GNUNET_NETWORK_fdset_destroy (wrs);
1489   GNUNET_NETWORK_fdset_destroy (wws);
1490   GNUNET_NETWORK_fdset_destroy (wes);
1491   return ret;
1492 }
1493
1494
1495 /**
1496  * Call MHD IPv4 to process pending requests and then go back
1497  * and schedule the next run.
1498  * @param cls plugin as closure
1499  * @param tc task context
1500  */
1501 static void
1502 http_server_daemon_v4_run (void *cls,
1503                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1504 {
1505   struct Plugin *plugin = cls;
1506
1507 #if DEBUG_SCHEDULING
1508   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
1509     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1510                 "http_server_daemon_v4_run: GNUNET_SCHEDULER_REASON_READ_READY\n");
1511   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
1512     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1513                 "http_server_daemon_v4_run: GNUNET_SCHEDULER_REASON_WRITE_READY\n");
1514   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
1515     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1516                 "http_server_daemon_v4_run: GNUNET_SCHEDULER_REASON_TIMEOUT\n");
1517   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_STARTUP))
1518     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1519                 "http_server_daemon_v4_run: GGNUNET_SCHEDULER_REASON_STARTUP\n");
1520   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1521     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1522                 "http_server_daemon_v4_run: GGNUNET_SCHEDULER_REASON_SHUTDOWN\n");
1523 #endif
1524
1525   GNUNET_assert (cls != NULL);
1526   plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1527
1528   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1529     return;
1530
1531   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4));
1532   plugin->http_server_task_v4 =
1533       http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
1534 }
1535
1536
1537 /**
1538  * Call MHD IPv6 to process pending requests and then go back
1539  * and schedule the next run.
1540  * @param cls plugin as closure
1541  * @param tc task context
1542  */
1543 static void
1544 http_server_daemon_v6_run (void *cls,
1545                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1546 {
1547   struct Plugin *plugin = cls;
1548
1549 #if DEBUG_SCHEDULING
1550   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
1551     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1552                 "http_server_daemon_v6_run: GNUNET_SCHEDULER_REASON_READ_READY\n");
1553   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
1554     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1555                 "http_server_daemon_v6_run: GNUNET_SCHEDULER_REASON_WRITE_READY\n");
1556   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
1557     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1558                 "http_server_daemon_v6_run: GNUNET_SCHEDULER_REASON_TIMEOUT\n");
1559   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_STARTUP))
1560     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1561                 "http_server_daemon_v6_run: GGNUNET_SCHEDULER_REASON_STARTUP\n");
1562   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1563     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1564                 "http_server_daemon_v6_run: GGNUNET_SCHEDULER_REASON_SHUTDOWN\n");
1565 #endif
1566
1567   GNUNET_assert (cls != NULL);
1568   plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1569
1570   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1571     return;
1572
1573   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6));
1574   plugin->http_server_task_v6 =
1575       http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
1576 }
1577
1578
1579 static size_t
1580 curl_get_header_cb (void *ptr, size_t size, size_t nmemb, void *stream)
1581 {
1582   struct Session *ps = stream;
1583
1584   long http_result = 0;
1585   int res;
1586
1587   /* Getting last http result code */
1588   GNUNET_assert (NULL != ps);
1589   if (ps->recv_connected == GNUNET_NO)
1590   {
1591     res =
1592         curl_easy_getinfo (ps->recv_endpoint, CURLINFO_RESPONSE_CODE,
1593                            &http_result);
1594     if (CURLE_OK == res)
1595     {
1596       if (http_result == 200)
1597       {
1598         ps->recv_connected = GNUNET_YES;
1599         ps->recv_active = GNUNET_YES;
1600 #if DEBUG_CONNECTIONS
1601         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1602                     "Connection %X: connected to recieve data\n", ps);
1603 #endif
1604         // Calling send_check_connections again since receive is established
1605         send_check_connections (ps->peercontext->plugin, ps);
1606       }
1607     }
1608   }
1609
1610 #if DEBUG_CURL
1611   char *tmp;
1612   size_t len = size * nmemb;
1613
1614   tmp = NULL;
1615   if ((size * nmemb) < SIZE_MAX)
1616     tmp = GNUNET_malloc (len + 1);
1617
1618   if ((tmp != NULL) && (len > 0))
1619   {
1620     memcpy (tmp, ptr, len);
1621     if (len >= 2)
1622     {
1623       if (tmp[len - 2] == 13)
1624         tmp[len - 2] = '\0';
1625     }
1626     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection %X: Header: %s\n", ps,
1627                 tmp);
1628   }
1629   GNUNET_free_non_null (tmp);
1630 #endif
1631
1632   return size * nmemb;
1633 }
1634
1635
1636 /**
1637  * Callback called by libcurl when new headers arrive
1638  * Used to get HTTP result for curl operations
1639  * @param ptr stream to read from
1640  * @param size size of one char element
1641  * @param nmemb number of char elements
1642  * @param stream closure set by user
1643  * @return bytes read by function
1644  */
1645 static size_t
1646 curl_put_header_cb (void *ptr, size_t size, size_t nmemb, void *stream)
1647 {
1648   struct Session *ps = stream;
1649
1650   char *tmp;
1651   size_t len = size * nmemb;
1652   long http_result = 0;
1653   int res;
1654
1655   /* Getting last http result code */
1656   GNUNET_assert (NULL != ps);
1657   res =
1658       curl_easy_getinfo (ps->send_endpoint, CURLINFO_RESPONSE_CODE,
1659                          &http_result);
1660   if (CURLE_OK == res)
1661   {
1662     if ((http_result == 100) && (ps->send_connected == GNUNET_NO))
1663     {
1664       ps->send_connected = GNUNET_YES;
1665       ps->send_active = GNUNET_YES;
1666 #if DEBUG_CONNECTIONS
1667       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1668                   "Connection %X: connected to send data\n", ps);
1669 #endif
1670     }
1671     if ((http_result == 200) && (ps->send_connected == GNUNET_YES))
1672     {
1673       ps->send_connected = GNUNET_NO;
1674       ps->send_active = GNUNET_NO;
1675 #if DEBUG_CONNECTIONS
1676       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1677                   "Connection %X: sending disconnected\n", ps);
1678 #endif
1679     }
1680   }
1681
1682   tmp = NULL;
1683   if ((size * nmemb) < SIZE_MAX)
1684     tmp = GNUNET_malloc (len + 1);
1685
1686   if ((tmp != NULL) && (len > 0))
1687   {
1688     memcpy (tmp, ptr, len);
1689     if (len >= 2)
1690     {
1691       if (tmp[len - 2] == 13)
1692         tmp[len - 2] = '\0';
1693     }
1694   }
1695   GNUNET_free_non_null (tmp);
1696   return size * nmemb;
1697 }
1698
1699 /**
1700  * Callback method used with libcurl
1701  * Method is called when libcurl needs to read data during sending
1702  * @param stream pointer where to write data
1703  * @param size size of an individual element
1704  * @param nmemb count of elements that can be written to the buffer
1705  * @param ptr source pointer, passed to the libcurl handle
1706  * @return bytes written to stream
1707  */
1708 static size_t
1709 curl_send_cb (void *stream, size_t size, size_t nmemb, void *ptr)
1710 {
1711   struct Session *ps = ptr;
1712   struct HTTP_Message *msg = ps->pending_msgs_tail;
1713   size_t bytes_sent;
1714   size_t len;
1715
1716   if (ps->send_active == GNUNET_NO)
1717     return CURL_READFUNC_PAUSE;
1718   if ((ps->pending_msgs_tail == NULL) && (ps->send_active == GNUNET_YES))
1719   {
1720 #if DEBUG_CONNECTIONS
1721     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1722                 "Connection %X: No Message to send, pausing connection\n", ps);
1723 #endif
1724     ps->send_active = GNUNET_NO;
1725     return CURL_READFUNC_PAUSE;
1726   }
1727
1728   GNUNET_assert (msg != NULL);
1729
1730   /* data to send */
1731   if (msg->pos < msg->size)
1732   {
1733     /* data fit in buffer */
1734     if ((msg->size - msg->pos) <= (size * nmemb))
1735     {
1736       len = (msg->size - msg->pos);
1737       memcpy (stream, &msg->buf[msg->pos], len);
1738       msg->pos += len;
1739       bytes_sent = len;
1740     }
1741     else
1742     {
1743       len = size * nmemb;
1744       memcpy (stream, &msg->buf[msg->pos], len);
1745       msg->pos += len;
1746       bytes_sent = len;
1747     }
1748   }
1749   /* no data to send */
1750   else
1751   {
1752     bytes_sent = 0;
1753   }
1754
1755   if (msg->pos == msg->size)
1756   {
1757 #if DEBUG_CONNECTIONS
1758     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1759                 "Connection %X: Message with %u bytes sent, removing message from queue\n",
1760                 ps, msg->pos);
1761 #endif
1762     /* Calling transmit continuation  */
1763     if (NULL != ps->pending_msgs_tail->transmit_cont)
1764       msg->transmit_cont (ps->pending_msgs_tail->transmit_cont_cls,
1765                           &(ps->peercontext)->identity, GNUNET_OK);
1766     ps->queue_length_cur -= msg->size;
1767     remove_http_message (ps, msg);
1768   }
1769   return bytes_sent;
1770 }
1771
1772
1773 static void
1774 curl_receive_mst_cb (void *cls, void *client,
1775                      const struct GNUNET_MessageHeader *message)
1776 {
1777   struct Session *ps = cls;
1778   struct GNUNET_TIME_Relative delay;
1779
1780   GNUNET_assert (ps != NULL);
1781
1782   struct HTTP_PeerContext *pc = ps->peercontext;
1783
1784   GNUNET_assert (pc != NULL);
1785 #if DEBUG_HTTP
1786   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1787               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
1788               ps, ntohs (message->type), ntohs (message->size),
1789               GNUNET_i2s (&(pc->identity)), http_plugin_address_to_string (NULL,
1790                                                                            ps->addr,
1791                                                                            ps->addrlen));
1792 #endif
1793   struct GNUNET_TRANSPORT_ATS_Information distance[2];
1794
1795   distance[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
1796   distance[0].value = htonl (1);
1797   distance[1].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
1798   distance[1].value = htonl (0);
1799
1800   delay =
1801       pc->plugin->env->receive (pc->plugin->env->cls, &pc->identity, message,
1802                                 (const struct GNUNET_TRANSPORT_ATS_Information
1803                                  *) &distance, 2, ps, ps->addr, ps->addrlen);
1804   pc->delay = delay;
1805   if (pc->reset_task != GNUNET_SCHEDULER_NO_TASK)
1806     GNUNET_SCHEDULER_cancel (pc->reset_task);
1807
1808   if (delay.rel_value > 0)
1809   {
1810 #if DEBUG_HTTP
1811     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1812                 "Connection %X: Inbound quota management: delay next read for %llu ms\n",
1813                 ps, delay.rel_value);
1814 #endif
1815     pc->reset_task =
1816         GNUNET_SCHEDULER_add_delayed (delay, &reset_inbound_quota_delay, pc);
1817   }
1818 }
1819
1820
1821 /**
1822 * Callback method used with libcurl
1823 * Method is called when libcurl needs to write data during sending
1824 * @param stream pointer where to write data
1825 * @param size size of an individual element
1826 * @param nmemb count of elements that can be written to the buffer
1827 * @param ptr destination pointer, passed to the libcurl handle
1828 * @return bytes read from stream
1829 */
1830 static size_t
1831 curl_receive_cb (void *stream, size_t size, size_t nmemb, void *ptr)
1832 {
1833   struct Session *ps = ptr;
1834
1835   if (ps->peercontext->delay.rel_value > 0)
1836   {
1837 #if DEBUG_HTTP
1838     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1839                 "Connection %X: no inbound bandwidth available! Next read was delayed for  %llu ms\n",
1840                 ps, ps->peercontext->delay.rel_value);
1841 #endif
1842     return 0;
1843   }
1844 #if DEBUG_CONNECTIONS
1845   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection %X: %u bytes received\n", ps,
1846               size * nmemb);
1847 #endif
1848   GNUNET_SERVER_mst_receive (ps->msgtok, ps, stream, size * nmemb, GNUNET_NO,
1849                              GNUNET_NO);
1850   return (size * nmemb);
1851 }
1852
1853
1854 static void
1855 curl_handle_finished (struct Plugin *plugin)
1856 {
1857   struct Session *ps = NULL;
1858   struct HTTP_PeerContext *pc = NULL;
1859   struct CURLMsg *msg;
1860   struct HTTP_Message *cur_msg = NULL;
1861   int msgs_in_queue;
1862   char *tmp;
1863   long http_result;
1864
1865   do
1866   {
1867     msg = curl_multi_info_read (plugin->multi_handle, &msgs_in_queue);
1868     if ((msgs_in_queue == 0) || (msg == NULL))
1869       break;
1870     /* get session for affected curl handle */
1871     GNUNET_assert (msg->easy_handle != NULL);
1872     curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, &tmp);
1873     ps = (struct Session *) tmp;
1874     GNUNET_assert (ps != NULL);
1875     pc = ps->peercontext;
1876     GNUNET_assert (pc != NULL);
1877     switch (msg->msg)
1878     {
1879     case CURLMSG_DONE:
1880       if ((msg->data.result != CURLE_OK) &&
1881           (msg->data.result != CURLE_GOT_NOTHING))
1882       {
1883         /* sending msg failed */
1884         if (msg->easy_handle == ps->send_endpoint)
1885         {
1886 #if DEBUG_CONNECTIONS
1887           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1888                       _
1889                       ("Connection %X: HTTP PUT to peer `%s' (`%s') failed: `%s' `%s'\n"),
1890                       ps, GNUNET_i2s (&pc->identity),
1891                       http_plugin_address_to_string (NULL, ps->addr,
1892                                                      ps->addrlen),
1893                       "curl_multi_perform",
1894                       curl_easy_strerror (msg->data.result));
1895 #endif
1896           ps->send_connected = GNUNET_NO;
1897           ps->send_active = GNUNET_NO;
1898           curl_multi_remove_handle (plugin->multi_handle, ps->send_endpoint);
1899           while (ps->pending_msgs_tail != NULL)
1900           {
1901             cur_msg = ps->pending_msgs_tail;
1902             if (NULL != cur_msg->transmit_cont)
1903               cur_msg->transmit_cont (cur_msg->transmit_cont_cls, &pc->identity,
1904                                       GNUNET_SYSERR);
1905             ps->queue_length_cur -= cur_msg->size;
1906             remove_http_message (ps, cur_msg);
1907           }
1908         }
1909         /* GET connection failed */
1910         if (msg->easy_handle == ps->recv_endpoint)
1911         {
1912 #if DEBUG_CONNECTIONS
1913           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1914                       _
1915                       ("Connection %X: HTTP GET to peer `%s' (`%s') failed: `%s' `%s'\n"),
1916                       ps, GNUNET_i2s (&pc->identity),
1917                       http_plugin_address_to_string (NULL, ps->addr,
1918                                                      ps->addrlen),
1919                       "curl_multi_perform",
1920                       curl_easy_strerror (msg->data.result));
1921 #endif
1922           ps->recv_connected = GNUNET_NO;
1923           ps->recv_active = GNUNET_NO;
1924           curl_multi_remove_handle (plugin->multi_handle, ps->recv_endpoint);
1925         }
1926       }
1927       else
1928       {
1929         if (msg->easy_handle == ps->send_endpoint)
1930         {
1931           GNUNET_assert (CURLE_OK ==
1932                          curl_easy_getinfo (msg->easy_handle,
1933                                             CURLINFO_RESPONSE_CODE,
1934                                             &http_result));
1935 #if DEBUG_CONNECTIONS
1936           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1937                       "Connection %X: HTTP PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1938                       ps, GNUNET_i2s (&pc->identity),
1939                       http_plugin_address_to_string (NULL, ps->addr,
1940                                                      ps->addrlen), http_result);
1941 #endif
1942           /* Calling transmit continuation  */
1943           while (ps->pending_msgs_tail != NULL)
1944           {
1945             cur_msg = ps->pending_msgs_tail;
1946             if (NULL != cur_msg->transmit_cont)
1947             {
1948               /* HTTP 1xx : Last message before here was informational */
1949               if ((http_result >= 100) && (http_result < 200))
1950                 cur_msg->transmit_cont (cur_msg->transmit_cont_cls,
1951                                         &pc->identity, GNUNET_OK);
1952               /* HTTP 2xx: successful operations */
1953               if ((http_result >= 200) && (http_result < 300))
1954                 cur_msg->transmit_cont (cur_msg->transmit_cont_cls,
1955                                         &pc->identity, GNUNET_OK);
1956               /* HTTP 3xx..5xx: error */
1957               if ((http_result >= 300) && (http_result < 600))
1958                 cur_msg->transmit_cont (cur_msg->transmit_cont_cls,
1959                                         &pc->identity, GNUNET_SYSERR);
1960             }
1961             ps->queue_length_cur -= cur_msg->size;
1962             remove_http_message (ps, cur_msg);
1963           }
1964
1965           ps->send_connected = GNUNET_NO;
1966           ps->send_active = GNUNET_NO;
1967           curl_multi_remove_handle (plugin->multi_handle, ps->send_endpoint);
1968         }
1969         if (msg->easy_handle == ps->recv_endpoint)
1970         {
1971 #if DEBUG_CONNECTIONS
1972           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1973                       "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1974                       ps, GNUNET_i2s (&pc->identity),
1975                       http_plugin_address_to_string (NULL, ps->addr,
1976                                                      ps->addrlen), http_result);
1977 #endif
1978           ps->recv_connected = GNUNET_NO;
1979           ps->recv_active = GNUNET_NO;
1980           curl_multi_remove_handle (plugin->multi_handle, ps->recv_endpoint);
1981         }
1982         plugin->current_connections--;
1983       }
1984       if ((ps->recv_connected == GNUNET_NO) &&
1985           (ps->send_connected == GNUNET_NO))
1986         remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR);
1987       break;
1988     default:
1989       break;
1990     }
1991   }
1992   while ((msgs_in_queue > 0));
1993 }
1994
1995
1996 /**
1997  * Task performing curl operations
1998  * @param cls plugin as closure
1999  * @param tc gnunet scheduler task context
2000  */
2001 static void
2002 curl_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2003 {
2004   struct Plugin *plugin = cls;
2005   static unsigned int handles_last_run;
2006   int running;
2007   CURLMcode mret;
2008
2009   GNUNET_assert (cls != NULL);
2010
2011   plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2012   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2013     return;
2014   do
2015   {
2016     running = 0;
2017     mret = curl_multi_perform (plugin->multi_handle, &running);
2018     if ((running < handles_last_run) && (running > 0))
2019       curl_handle_finished (plugin);
2020     handles_last_run = running;
2021   }
2022   while (mret == CURLM_CALL_MULTI_PERFORM);
2023   curl_schedule (plugin);
2024 }
2025
2026
2027 /**
2028  * Function setting up file descriptors and scheduling task to run
2029  *
2030  * @param  plugin plugin as closure
2031  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
2032  */
2033 static int
2034 curl_schedule (struct Plugin *plugin)
2035 {
2036   fd_set rs;
2037   fd_set ws;
2038   fd_set es;
2039   int max;
2040   struct GNUNET_NETWORK_FDSet *grs;
2041   struct GNUNET_NETWORK_FDSet *gws;
2042   long to;
2043   CURLMcode mret;
2044
2045   /* Cancel previous scheduled task */
2046   if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2047   {
2048     GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
2049     plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2050   }
2051
2052   max = -1;
2053   FD_ZERO (&rs);
2054   FD_ZERO (&ws);
2055   FD_ZERO (&es);
2056   mret = curl_multi_fdset (plugin->multi_handle, &rs, &ws, &es, &max);
2057   if (mret != CURLM_OK)
2058   {
2059     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
2060                 "curl_multi_fdset", __FILE__, __LINE__,
2061                 curl_multi_strerror (mret));
2062     return GNUNET_SYSERR;
2063   }
2064   mret = curl_multi_timeout (plugin->multi_handle, &to);
2065   if (mret != CURLM_OK)
2066   {
2067     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
2068                 "curl_multi_timeout", __FILE__, __LINE__,
2069                 curl_multi_strerror (mret));
2070     return GNUNET_SYSERR;
2071   }
2072
2073   grs = GNUNET_NETWORK_fdset_create ();
2074   gws = GNUNET_NETWORK_fdset_create ();
2075   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
2076   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
2077   plugin->http_curl_task =
2078       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
2079                                    GNUNET_SCHEDULER_NO_TASK,
2080                                    (to ==
2081                                     -1) ?
2082                                    GNUNET_TIME_relative_multiply
2083                                    (GNUNET_TIME_UNIT_SECONDS,
2084                                     5) :
2085                                    GNUNET_TIME_relative_multiply
2086                                    (GNUNET_TIME_UNIT_MILLISECONDS, to), grs,
2087                                    gws, &curl_perform, plugin);
2088   GNUNET_NETWORK_fdset_destroy (gws);
2089   GNUNET_NETWORK_fdset_destroy (grs);
2090   return GNUNET_OK;
2091 }
2092
2093
2094 #if DEBUG_CURL
2095 /**
2096  * Function to log curl debug messages with GNUNET_log
2097  * @param curl handle
2098  * @param type curl_infotype
2099  * @param data data
2100  * @param size size
2101  * @param cls  closure
2102  * @return 0
2103  */
2104 static int
2105 curl_logger (CURL * curl, curl_infotype type, char *data, size_t size,
2106              void *cls)
2107 {
2108   if (type == CURLINFO_TEXT)
2109   {
2110     char text[size + 2];
2111
2112     memcpy (text, data, size);
2113     if (text[size - 1] == '\n')
2114       text[size] = '\0';
2115     else
2116     {
2117       text[size] = '\n';
2118       text[size + 1] = '\0';
2119     }
2120     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CURL: Connection %X - %s", cls, text);
2121   }
2122   return 0;
2123 }
2124 #endif
2125
2126
2127 /**
2128  * Function setting up curl handle and selecting message to send
2129  *
2130  * @param plugin plugin
2131  * @param ps session
2132  * @return GNUNET_SYSERR on failure, GNUNET_NO if connecting, GNUNET_YES if ok
2133  */
2134 static int
2135 send_check_connections (struct Plugin *plugin, struct Session *ps)
2136 {
2137   CURLMcode mret;
2138   struct GNUNET_TIME_Relative timeout =
2139       GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
2140
2141   if ((ps->direction == OUTBOUND) &&
2142       (plugin->current_connections < plugin->max_connect_per_transport))
2143   {
2144     /* RECV DIRECTION */
2145     /* Check if session is connected to receive data, otherwise connect to peer */
2146
2147     if (ps->recv_connected == GNUNET_NO)
2148     {
2149       int fresh = GNUNET_NO;
2150
2151       if (ps->recv_endpoint == NULL)
2152       {
2153         fresh = GNUNET_YES;
2154         ps->recv_endpoint = curl_easy_init ();
2155       }
2156 #if DEBUG_CURL
2157       curl_easy_setopt (ps->recv_endpoint, CURLOPT_VERBOSE, 1L);
2158       curl_easy_setopt (ps->recv_endpoint, CURLOPT_DEBUGFUNCTION, &curl_logger);
2159       curl_easy_setopt (ps->recv_endpoint, CURLOPT_DEBUGDATA,
2160                         ps->recv_endpoint);
2161 #endif
2162 #if BUILD_HTTPS
2163       curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSLVERSION,
2164                         CURL_SSLVERSION_TLSv1);
2165       curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSL_VERIFYPEER, 0);
2166       curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSL_VERIFYHOST, 0);
2167 #endif
2168       curl_easy_setopt (ps->recv_endpoint, CURLOPT_URL, ps->url);
2169       curl_easy_setopt (ps->recv_endpoint, CURLOPT_HEADERFUNCTION,
2170                         &curl_get_header_cb);
2171       curl_easy_setopt (ps->recv_endpoint, CURLOPT_WRITEHEADER, ps);
2172       curl_easy_setopt (ps->recv_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
2173       curl_easy_setopt (ps->recv_endpoint, CURLOPT_READDATA, ps);
2174       curl_easy_setopt (ps->recv_endpoint, CURLOPT_WRITEFUNCTION,
2175                         curl_receive_cb);
2176       curl_easy_setopt (ps->recv_endpoint, CURLOPT_WRITEDATA, ps);
2177       curl_easy_setopt (ps->recv_endpoint, CURLOPT_TIMEOUT,
2178                         (long) timeout.rel_value);
2179       curl_easy_setopt (ps->recv_endpoint, CURLOPT_PRIVATE, ps);
2180       curl_easy_setopt (ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT,
2181                         HTTP_CONNECT_TIMEOUT);
2182       curl_easy_setopt (ps->recv_endpoint, CURLOPT_BUFFERSIZE,
2183                         2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
2184 #if CURL_TCP_NODELAY
2185       curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
2186 #endif
2187       if (fresh == GNUNET_YES)
2188       {
2189         mret = curl_multi_add_handle (plugin->multi_handle, ps->recv_endpoint);
2190         if (mret != CURLM_OK)
2191         {
2192           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2193                       _("Connection: %X: %s failed at %s:%d: `%s'\n"), ps,
2194                       "curl_multi_add_handle", __FILE__, __LINE__,
2195                       curl_multi_strerror (mret));
2196           return GNUNET_SYSERR;
2197         }
2198       }
2199       if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2200       {
2201         GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
2202         plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2203       }
2204       plugin->current_connections++;
2205       plugin->http_curl_task = GNUNET_SCHEDULER_add_now (&curl_perform, plugin);
2206     }
2207
2208     /* waiting for receive direction */
2209     if (ps->recv_connected == GNUNET_NO)
2210       return GNUNET_NO;
2211
2212     /* SEND DIRECTION */
2213     /* Check if session is connected to send data, otherwise connect to peer */
2214     if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint != NULL))
2215     {
2216       if (ps->send_active == GNUNET_YES)
2217       {
2218 #if DEBUG_CONNECTIONS
2219         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2220                     "Connection %X: outbound active, enqueueing message\n", ps);
2221 #endif
2222         return GNUNET_YES;
2223       }
2224       if (ps->send_active == GNUNET_NO)
2225       {
2226 #if DEBUG_CONNECTIONS
2227         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2228                     "Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",
2229                     ps);
2230 #endif
2231         if (CURLE_OK == curl_easy_pause (ps->send_endpoint, CURLPAUSE_CONT))
2232         {
2233           ps->send_active = GNUNET_YES;
2234           if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2235           {
2236             GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
2237             plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2238           }
2239           plugin->http_curl_task =
2240               GNUNET_SCHEDULER_add_now (&curl_perform, plugin);
2241           return GNUNET_YES;
2242         }
2243         else
2244           return GNUNET_SYSERR;
2245       }
2246     }
2247     /* not connected, initiate connection */
2248     if ((ps->send_connected == GNUNET_NO) &&
2249         (plugin->current_connections < plugin->max_connect_per_transport))
2250     {
2251       int fresh = GNUNET_NO;
2252
2253       if (NULL == ps->send_endpoint)
2254       {
2255         ps->send_endpoint = curl_easy_init ();
2256         fresh = GNUNET_YES;
2257       }
2258       GNUNET_assert (ps->send_endpoint != NULL);
2259       GNUNET_assert (NULL != ps->pending_msgs_tail);
2260 #if DEBUG_CONNECTIONS
2261       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2262                   "Connection %X: outbound not connected, initiating connection\n",
2263                   ps);
2264 #endif
2265       ps->send_active = GNUNET_NO;
2266
2267 #if DEBUG_CURL
2268       curl_easy_setopt (ps->send_endpoint, CURLOPT_VERBOSE, 1L);
2269       curl_easy_setopt (ps->send_endpoint, CURLOPT_DEBUGFUNCTION, &curl_logger);
2270       curl_easy_setopt (ps->send_endpoint, CURLOPT_DEBUGDATA,
2271                         ps->send_endpoint);
2272 #endif
2273 #if BUILD_HTTPS
2274       curl_easy_setopt (ps->send_endpoint, CURLOPT_SSLVERSION,
2275                         CURL_SSLVERSION_TLSv1);
2276       curl_easy_setopt (ps->send_endpoint, CURLOPT_SSL_VERIFYPEER, 0);
2277       curl_easy_setopt (ps->send_endpoint, CURLOPT_SSL_VERIFYHOST, 0);
2278 #endif
2279       curl_easy_setopt (ps->send_endpoint, CURLOPT_URL, ps->url);
2280       curl_easy_setopt (ps->send_endpoint, CURLOPT_PUT, 1L);
2281       curl_easy_setopt (ps->send_endpoint, CURLOPT_HEADERFUNCTION,
2282                         &curl_put_header_cb);
2283       curl_easy_setopt (ps->send_endpoint, CURLOPT_WRITEHEADER, ps);
2284       curl_easy_setopt (ps->send_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
2285       curl_easy_setopt (ps->send_endpoint, CURLOPT_READDATA, ps);
2286       curl_easy_setopt (ps->send_endpoint, CURLOPT_WRITEFUNCTION,
2287                         curl_receive_cb);
2288       curl_easy_setopt (ps->send_endpoint, CURLOPT_READDATA, ps);
2289       curl_easy_setopt (ps->send_endpoint, CURLOPT_TIMEOUT,
2290                         (long) timeout.rel_value);
2291       curl_easy_setopt (ps->send_endpoint, CURLOPT_PRIVATE, ps);
2292       curl_easy_setopt (ps->send_endpoint, CURLOPT_CONNECTTIMEOUT,
2293                         HTTP_CONNECT_TIMEOUT);
2294       curl_easy_setopt (ps->send_endpoint, CURLOPT_BUFFERSIZE,
2295                         2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
2296 #if CURL_TCP_NODELAY
2297       curl_easy_setopt (ps->send_endpoint, CURLOPT_TCP_NODELAY, 1);
2298 #endif
2299       if (fresh == GNUNET_YES)
2300       {
2301         mret = curl_multi_add_handle (plugin->multi_handle, ps->send_endpoint);
2302         if (mret != CURLM_OK)
2303         {
2304           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2305                       _("Connection: %X: %s failed at %s:%d: `%s'\n"), ps,
2306                       "curl_multi_add_handle", __FILE__, __LINE__,
2307                       curl_multi_strerror (mret));
2308           return GNUNET_SYSERR;
2309         }
2310       }
2311     }
2312     if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2313     {
2314       GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
2315       plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2316     }
2317     plugin->current_connections++;
2318     plugin->http_curl_task = GNUNET_SCHEDULER_add_now (&curl_perform, plugin);
2319     return GNUNET_YES;
2320   }
2321   if (ps->direction == INBOUND)
2322   {
2323     GNUNET_assert (NULL != ps->pending_msgs_tail);
2324     if ((ps->recv_connected == GNUNET_YES) && (ps->send_connected == GNUNET_YES)
2325         && (ps->recv_force_disconnect == GNUNET_NO) &&
2326         (ps->recv_force_disconnect == GNUNET_NO))
2327       return GNUNET_YES;
2328   }
2329   return GNUNET_SYSERR;
2330 }
2331
2332
2333 /**
2334  * select best session to transmit data to peer
2335  *
2336  * @param pc peer context of target peer
2337  * @param addr address of target peer
2338  * @param addrlen address length
2339  * @param force_address does transport service enforce address?
2340  * @param session session passed by transport service
2341  * @return selected session
2342  *
2343  */
2344 static struct Session *
2345 send_select_session (struct HTTP_PeerContext *pc, const void *addr,
2346                      size_t addrlen, int force_address, struct Session *session)
2347 {
2348   struct Session *tmp = NULL;
2349   int addr_given = GNUNET_NO;
2350
2351   if ((addr != NULL) && (addrlen > 0))
2352     addr_given = GNUNET_YES;
2353
2354   if (force_address == GNUNET_YES)
2355   {
2356     /* check session given as argument */
2357     if ((session != NULL) && (addr_given == GNUNET_YES))
2358     {
2359       if (0 == memcmp (session->addr, addr, addrlen))
2360       {
2361         /* connection can not be used, since it is disconnected */
2362         if ((session->recv_force_disconnect == GNUNET_NO) &&
2363             (session->send_force_disconnect == GNUNET_NO))
2364         {
2365 #if DEBUG_SESSION_SELECTION
2366           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2367                       "Session %X selected: Using session passed by transport to send to forced address \n",
2368                       session);
2369 #endif
2370           return session;
2371         }
2372       }
2373     }
2374     /* check last session used */
2375     if ((pc->last_session != NULL) && (addr_given == GNUNET_YES))
2376     {
2377       if (0 == memcmp (pc->last_session->addr, addr, addrlen))
2378       {
2379         /* connection can not be used, since it is disconnected */
2380         if ((pc->last_session->recv_force_disconnect == GNUNET_NO) &&
2381             (pc->last_session->send_force_disconnect == GNUNET_NO))
2382         {
2383 #if DEBUG_SESSION_SELECTION
2384           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2385                       "Session %X selected: Using last session used to send to forced address \n",
2386                       pc->last_session);
2387 #endif
2388           return pc->last_session;
2389         }
2390       }
2391     }
2392     /* find session in existing sessions */
2393     tmp = pc->head;
2394     while ((tmp != NULL) && (addr_given == GNUNET_YES))
2395     {
2396       if (0 == memcmp (tmp->addr, addr, addrlen))
2397       {
2398         /* connection can not be used, since it is disconnected */
2399         if ((tmp->recv_force_disconnect == GNUNET_NO) &&
2400             (tmp->send_force_disconnect == GNUNET_NO))
2401         {
2402 #if DEBUG_SESSION_SELECTION
2403           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2404                       "Session %X selected: Using existing session to send to forced address \n",
2405                       session);
2406 #endif
2407           return session;
2408         }
2409       }
2410       tmp = tmp->next;
2411     }
2412     /* no session to use */
2413     return NULL;
2414   }
2415   if ((force_address == GNUNET_NO) || (force_address == GNUNET_SYSERR))
2416   {
2417     /* check session given as argument */
2418     if (session != NULL)
2419     {
2420       /* connection can not be used, since it is disconnected */
2421       if ((session->recv_force_disconnect == GNUNET_NO) &&
2422           (session->send_force_disconnect == GNUNET_NO))
2423       {
2424 #if DEBUG_SESSION_SELECTION
2425         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2426                     "Session %X selected: Using session passed by transport to send not-forced address\n",
2427                     session);
2428 #endif
2429         return session;
2430       }
2431     }
2432     /* check last session used */
2433     if (pc->last_session != NULL)
2434     {
2435       /* connection can not be used, since it is disconnected */
2436       if ((pc->last_session->recv_force_disconnect == GNUNET_NO) &&
2437           (pc->last_session->send_force_disconnect == GNUNET_NO))
2438       {
2439 #if DEBUG_SESSION_SELECTION
2440         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2441                     "Session %X selected: Using last session to send to not-forced address\n",
2442                     pc->last_session);
2443 #endif
2444         return pc->last_session;
2445       }
2446     }
2447     /* find session in existing sessions */
2448     tmp = pc->head;
2449     while (tmp != NULL)
2450     {
2451       /* connection can not be used, since it is disconnected */
2452       if ((tmp->recv_force_disconnect == GNUNET_NO) &&
2453           (tmp->send_force_disconnect == GNUNET_NO))
2454       {
2455 #if DEBUG_SESSION_SELECTION
2456         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2457                     "Session %X selected: Using existing session to send to not-forced address\n",
2458                     tmp);
2459 #endif
2460         return tmp;
2461       }
2462       tmp = tmp->next;
2463     }
2464     return NULL;
2465   }
2466   return NULL;
2467 }
2468
2469
2470 /**
2471  * Function that can be used by the transport service to transmit
2472  * a message using the plugin.   Note that in the case of a
2473  * peer disconnecting, the continuation MUST be called
2474  * prior to the disconnect notification itself.  This function
2475  * will be called with this peer's HELLO message to initiate
2476  * a fresh connection to another peer.
2477  *
2478  * @param cls closure
2479  * @param target who should receive this message
2480  * @param msgbuf the message to transmit
2481  * @param msgbuf_size number of bytes in 'msgbuf'
2482  * @param priority how important is the message (most plugins will
2483  *                 ignore message priority and just FIFO)
2484  * @param to how long to wait at most for the transmission (does not
2485  *                require plugins to discard the message after the timeout,
2486  *                just advisory for the desired delay; most plugins will ignore
2487  *                this as well)
2488  * @param session which session must be used (or NULL for "any")
2489  * @param addr the address to use (can be NULL if the plugin
2490  *                is "on its own" (i.e. re-use existing TCP connection))
2491  * @param addrlen length of the address in bytes
2492  * @param force_address GNUNET_YES if the plugin MUST use the given address,
2493  *                GNUNET_NO means the plugin may use any other address and
2494  *                GNUNET_SYSERR means that only reliable existing
2495  *                bi-directional connections should be used (regardless
2496  *                of address)
2497  * @param cont continuation to call once the message has
2498  *        been transmitted (or if the transport is ready
2499  *        for the next transmission call; or if the
2500  *        peer disconnected...); can be NULL
2501  * @param cont_cls closure for cont
2502  * @return number of bytes used (on the physical network, with overheads);
2503  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
2504  *         and does NOT mean that the message was not transmitted (DV)
2505  */
2506 static ssize_t
2507 http_plugin_send (void *cls, const struct GNUNET_PeerIdentity *target,
2508                   const char *msgbuf, size_t msgbuf_size, unsigned int priority,
2509                   struct GNUNET_TIME_Relative to, struct Session *session,
2510                   const void *addr, size_t addrlen, int force_address,
2511                   GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
2512 {
2513   struct Plugin *plugin = cls;
2514   struct HTTP_Message *msg;
2515   struct HTTP_PeerContext *pc;
2516   struct Session *ps = NULL;
2517
2518   GNUNET_assert (cls != NULL);
2519
2520 #if DEBUG_HTTP
2521   char *force;
2522
2523   if (force_address == GNUNET_YES)
2524     GNUNET_asprintf (&force, "forced addr.");
2525   else if (force_address == GNUNET_NO)
2526     GNUNET_asprintf (&force, "any addr.");
2527   else if (force_address == GNUNET_SYSERR)
2528     GNUNET_asprintf (&force, "reliable bi-direc. address addr.");
2529   else
2530     GNUNET_assert (0);
2531   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2532               "Transport tells me to send %u bytes to `%s' using %s (%s) and session: %X\n",
2533               msgbuf_size, GNUNET_i2s (target), force,
2534               http_plugin_address_to_string (NULL, addr, addrlen), session);
2535   GNUNET_free (force);
2536 #endif
2537
2538   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
2539   /* Peer unknown */
2540   if (pc == NULL)
2541   {
2542     pc = GNUNET_malloc (sizeof (struct HTTP_PeerContext));
2543     pc->plugin = plugin;
2544     pc->session_id_counter = 1;
2545     pc->last_session = NULL;
2546     memcpy (&pc->identity, target, sizeof (struct GNUNET_PeerIdentity));
2547     GNUNET_CONTAINER_multihashmap_put (plugin->peers, &pc->identity.hashPubKey,
2548                                        pc,
2549                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
2550     GNUNET_STATISTICS_update (plugin->env->stats,
2551                               gettext_noop ("# HTTP peers active"), 1,
2552                               GNUNET_NO);
2553   }
2554   ps = send_select_session (pc, addr, addrlen, force_address, session);
2555   /* session not existing, but address forced -> creating new session */
2556   if (ps == NULL)
2557   {
2558     if ((addr != NULL) && (addrlen != 0))
2559     {
2560       ps = GNUNET_malloc (sizeof (struct Session));
2561 #if DEBUG_SESSION_SELECTION
2562       if (force_address == GNUNET_YES)
2563         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2564                     "No existing connection & forced address: creating new session %X to peer %s\n",
2565                     ps, GNUNET_i2s (target));
2566       if (force_address != GNUNET_YES)
2567         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2568                     "No existing connection: creating new session %X to peer %s\n",
2569                     ps, GNUNET_i2s (target));
2570 #endif
2571       ps->addr = GNUNET_malloc (addrlen);
2572       memcpy (ps->addr, addr, addrlen);
2573       ps->addrlen = addrlen;
2574       ps->direction = OUTBOUND;
2575       ps->recv_connected = GNUNET_NO;
2576       ps->recv_force_disconnect = GNUNET_NO;
2577       ps->send_connected = GNUNET_NO;
2578       ps->send_force_disconnect = GNUNET_NO;
2579       ps->pending_msgs_head = NULL;
2580       ps->pending_msgs_tail = NULL;
2581       ps->peercontext = pc;
2582       ps->session_id = pc->session_id_counter;
2583       ps->queue_length_cur = 0;
2584       ps->queue_length_max = GNUNET_SERVER_MAX_MESSAGE_SIZE;
2585       pc->session_id_counter++;
2586       ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
2587       if (ps->msgtok == NULL)
2588         ps->msgtok = GNUNET_SERVER_mst_create (&curl_receive_mst_cb, ps);
2589       GNUNET_CONTAINER_DLL_insert (pc->head, pc->tail, ps);
2590       GNUNET_STATISTICS_update (plugin->env->stats,
2591                                 gettext_noop
2592                                 ("# HTTP outbound sessions for peers active"),
2593                                 1, GNUNET_NO);
2594     }
2595     else
2596     {
2597 #if DEBUG_HTTP
2598       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2599                   "No existing session found & and no address given: no way to send this message to peer `%s'!\n",
2600                   GNUNET_i2s (target));
2601 #endif
2602       return GNUNET_SYSERR;
2603     }
2604   }
2605
2606   if (msgbuf_size >= (ps->queue_length_max - ps->queue_length_cur))
2607   {
2608     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2609                 "Queue %X full: %u bytes in queue available, message with %u is too big\n",
2610                 ps, (ps->queue_length_max - ps->queue_length_cur), msgbuf_size);
2611     //return GNUNET_SYSERR;
2612   }
2613
2614   /* create msg */
2615   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
2616   msg->next = NULL;
2617   msg->size = msgbuf_size;
2618   msg->pos = 0;
2619   msg->buf = (char *) &msg[1];
2620   msg->transmit_cont = cont;
2621   msg->transmit_cont_cls = cont_cls;
2622   memcpy (msg->buf, msgbuf, msgbuf_size);
2623   GNUNET_CONTAINER_DLL_insert (ps->pending_msgs_head, ps->pending_msgs_tail,
2624                                msg);
2625   ps->queue_length_cur += msgbuf_size;
2626   if (send_check_connections (plugin, ps) == GNUNET_SYSERR)
2627     return GNUNET_SYSERR;
2628   if (force_address != GNUNET_YES)
2629     pc->last_session = ps;
2630   if (pc->last_session == NULL)
2631     pc->last_session = ps;
2632   return msg->size;
2633 }
2634
2635
2636 /**
2637  * Function that can be used to force the plugin to disconnect
2638  * from the given peer and cancel all previous transmissions
2639  * (and their continuationc).
2640  *
2641  * @param cls closure
2642  * @param target peer from which to disconnect
2643  */
2644 static void
2645 http_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
2646 {
2647   struct Plugin *plugin = cls;
2648   struct HTTP_PeerContext *pc = NULL;
2649   struct Session *ps = NULL;
2650
2651   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
2652   if (pc == NULL)
2653     return;
2654   ps = pc->head;
2655   while (ps != NULL)
2656   {
2657     /* Telling transport that session is getting disconnected */
2658     plugin->env->session_end (plugin, target, ps);
2659     if (ps->direction == OUTBOUND)
2660     {
2661       if (ps->send_endpoint != NULL)
2662       {
2663         //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint));
2664         //curl_easy_cleanup(ps->send_endpoint);
2665         //ps->send_endpoint=NULL;
2666         ps->send_force_disconnect = GNUNET_YES;
2667       }
2668       if (ps->recv_endpoint != NULL)
2669       {
2670         //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint));
2671         //curl_easy_cleanup(ps->recv_endpoint);
2672         //ps->recv_endpoint=NULL;
2673         ps->recv_force_disconnect = GNUNET_YES;
2674       }
2675     }
2676     if (ps->direction == INBOUND)
2677     {
2678       ps->recv_force_disconnect = GNUNET_YES;
2679       ps->send_force_disconnect = GNUNET_YES;
2680     }
2681     while (ps->pending_msgs_head != NULL)
2682       remove_http_message (ps, ps->pending_msgs_head);
2683     ps->recv_active = GNUNET_NO;
2684     ps->send_active = GNUNET_NO;
2685     ps = ps->next;
2686   }
2687 }
2688
2689
2690 /**
2691  * Append our port and forward the result.
2692  *
2693  * @param cls the 'struct PrettyPrinterContext*'
2694  * @param hostname hostname part of the address
2695  */
2696 static void
2697 append_port (void *cls, const char *hostname)
2698 {
2699   struct PrettyPrinterContext *ppc = cls;
2700   char *ret;
2701
2702   if (hostname == NULL)
2703   {
2704     ppc->asc (ppc->asc_cls, NULL);
2705     GNUNET_free (ppc);
2706     return;
2707   }
2708   GNUNET_asprintf (&ret, "%s://%s:%d", PROTOCOL_PREFIX, hostname, ppc->port);
2709
2710   ppc->asc (ppc->asc_cls, ret);
2711   GNUNET_free (ret);
2712 }
2713
2714
2715
2716 /**
2717  * Convert the transports address to a nice, human-readable
2718  * format.
2719  *
2720  * @param cls closure
2721  * @param type name of the transport that generated the address
2722  * @param addr one of the addresses of the host, NULL for the last address
2723  *        the specific address format depends on the transport
2724  * @param addrlen length of the address
2725  * @param numeric should (IP) addresses be displayed in numeric form?
2726  * @param timeout after how long should we give up?
2727  * @param asc function to call on each string
2728  * @param asc_cls closure for asc
2729  */
2730 static void
2731 http_plugin_address_pretty_printer (void *cls, const char *type,
2732                                     const void *addr, size_t addrlen,
2733                                     int numeric,
2734                                     struct GNUNET_TIME_Relative timeout,
2735                                     GNUNET_TRANSPORT_AddressStringCallback asc,
2736                                     void *asc_cls)
2737 {
2738   struct PrettyPrinterContext *ppc;
2739   const void *sb;
2740   size_t sbs;
2741   struct sockaddr_in a4;
2742   struct sockaddr_in6 a6;
2743   const struct IPv4HttpAddress *t4;
2744   const struct IPv6HttpAddress *t6;
2745   uint16_t port;
2746
2747   if (addrlen == sizeof (struct IPv6HttpAddress))
2748   {
2749     t6 = addr;
2750     memset (&a6, 0, sizeof (a6));
2751     a6.sin6_family = AF_INET6;
2752     a6.sin6_port = t6->port;
2753     memcpy (&a6.sin6_addr, &t6->ipv6_addr, sizeof (struct in6_addr));
2754     port = ntohs (t6->port);
2755     sb = &a6;
2756     sbs = sizeof (a6);
2757   }
2758   else if (addrlen == sizeof (struct IPv4HttpAddress))
2759   {
2760     t4 = addr;
2761     memset (&a4, 0, sizeof (a4));
2762     a4.sin_family = AF_INET;
2763     a4.sin_port = t4->port;
2764     a4.sin_addr.s_addr = t4->ipv4_addr;
2765     port = ntohs (t4->ipv4_addr);
2766     sb = &a4;
2767     sbs = sizeof (a4);
2768   }
2769   else
2770   {
2771     /* invalid address */
2772     GNUNET_break_op (0);
2773     asc (asc_cls, NULL);
2774     return;
2775   }
2776   ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
2777   ppc->asc = asc;
2778   ppc->asc_cls = asc_cls;
2779   ppc->port = port;
2780   GNUNET_RESOLVER_hostname_get (sb, sbs, !numeric, timeout, &append_port, ppc);
2781 }
2782
2783
2784
2785 /**
2786  * Another peer has suggested an address for this
2787  * peer and transport plugin.  Check that this could be a valid
2788  * address.  If so, consider adding it to the list
2789  * of addresses.
2790  *
2791  * @param cls closure
2792  * @param addr pointer to the address
2793  * @param addrlen length of addr
2794  * @return GNUNET_OK if this is a plausible address for this peer
2795  *         and transport
2796  */
2797 static int
2798 http_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
2799 {
2800   struct Plugin *plugin = cls;
2801   struct IPv4HttpAddress *v4;
2802   struct IPv6HttpAddress *v6;
2803   struct IPv4HttpAddressWrapper *w_tv4 = plugin->ipv4_addr_head;
2804   struct IPv6HttpAddressWrapper *w_tv6 = plugin->ipv6_addr_head;
2805
2806   GNUNET_assert (cls != NULL);
2807   if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
2808       (addrlen != sizeof (struct IPv6HttpAddress)))
2809     return GNUNET_SYSERR;
2810   if (addrlen == sizeof (struct IPv4HttpAddress))
2811   {
2812     v4 = (struct IPv4HttpAddress *) addr;
2813     if (plugin->bind4_address != NULL)
2814     {
2815       if (0 ==
2816           memcmp (&plugin->bind4_address->sin_addr, &v4->ipv4_addr,
2817                   sizeof (uint32_t)))
2818         return GNUNET_OK;
2819       else
2820         return GNUNET_SYSERR;
2821     }
2822     while (w_tv4 != NULL)
2823     {
2824       if (0 ==
2825           memcmp (&w_tv4->addr->ipv4_addr, &v4->ipv4_addr, sizeof (uint32_t)))
2826         break;
2827       w_tv4 = w_tv4->next;
2828     }
2829     if (w_tv4 != NULL)
2830       return GNUNET_OK;
2831     else
2832       return GNUNET_SYSERR;
2833   }
2834   if (addrlen == sizeof (struct IPv6HttpAddress))
2835   {
2836     v6 = (struct IPv6HttpAddress *) addr;
2837     if (plugin->bind6_address != NULL)
2838     {
2839       if (0 ==
2840           memcmp (&plugin->bind6_address->sin6_addr, &v6->ipv6_addr,
2841                   sizeof (struct in6_addr)))
2842         return GNUNET_OK;
2843       else
2844         return GNUNET_SYSERR;
2845     }
2846     while (w_tv6 != NULL)
2847     {
2848       if (0 ==
2849           memcmp (&w_tv6->addr->ipv6_addr, &v6->ipv6_addr,
2850                   sizeof (struct in6_addr)))
2851         break;
2852       w_tv6 = w_tv6->next;
2853     }
2854     if (w_tv6 != NULL)
2855       return GNUNET_OK;
2856     else
2857       return GNUNET_SYSERR;
2858   }
2859   return GNUNET_SYSERR;
2860 }
2861
2862
2863 /**
2864  * Function called for a quick conversion of the binary address to
2865  * a numeric address.  Note that the caller must not free the
2866  * address and that the next call to this function is allowed
2867  * to override the address again.
2868  *
2869  * @param cls closure
2870  * @param addr binary address
2871  * @param addrlen length of the address
2872  * @return string representing the same address
2873  */
2874 static const char *
2875 http_plugin_address_to_string (void *cls, const void *addr, size_t addrlen)
2876 {
2877   const struct IPv4HttpAddress *t4;
2878   const struct IPv6HttpAddress *t6;
2879   struct sockaddr_in a4;
2880   struct sockaddr_in6 a6;
2881   char *address;
2882   static char rbuf[INET6_ADDRSTRLEN + 13];
2883   uint16_t port;
2884   int res;
2885
2886   if (addrlen == sizeof (struct IPv6HttpAddress))
2887   {
2888     address = GNUNET_malloc (INET6_ADDRSTRLEN);
2889     t6 = addr;
2890     a6.sin6_addr = t6->ipv6_addr;
2891     inet_ntop (AF_INET6, &(a6.sin6_addr), address, INET6_ADDRSTRLEN);
2892     port = ntohs (t6->port);
2893   }
2894   else if (addrlen == sizeof (struct IPv4HttpAddress))
2895   {
2896     address = GNUNET_malloc (INET_ADDRSTRLEN);
2897     t4 = addr;
2898     a4.sin_addr.s_addr = t4->ipv4_addr;
2899     inet_ntop (AF_INET, &(a4.sin_addr), address, INET_ADDRSTRLEN);
2900     port = ntohs (t4->port);
2901   }
2902   else
2903   {
2904     /* invalid address */
2905     return NULL;
2906   }
2907
2908   res = GNUNET_snprintf (rbuf, sizeof (rbuf), 
2909                          (addrlen == sizeof (struct IPv6HttpAddress)) ? "[%s]:%u" : "%s:%u",
2910                          address, port);
2911
2912   GNUNET_free (address);
2913   GNUNET_assert (res != 0);
2914   return rbuf;
2915 }
2916
2917 /**
2918  * Function called by the NAT subsystem suggesting another peer wants
2919  * to connect to us via connection reversal.  Try to connect back to the
2920  * given IP.
2921  *
2922  * @param cls closure
2923  * @param addr address to try
2924  * @param addrlen number of bytes in addr
2925  */
2926 static void
2927 try_connection_reversal (void *cls, const struct sockaddr *addr,
2928                          socklen_t addrlen)
2929 {
2930
2931 }
2932
2933 static void
2934 tcp_nat_cb_add_addr (void *cls, int add_remove, const struct sockaddr *addr,
2935                      socklen_t addrlen)
2936 {
2937   struct Plugin *plugin = cls;
2938   struct IPv4HttpAddress *t4 = NULL;
2939   struct IPv4HttpAddressWrapper *w_t4 = NULL;
2940   struct IPv6HttpAddress *t6 = NULL;
2941   struct IPv6HttpAddressWrapper *w_t6 = NULL;
2942   int af;
2943
2944   af = addr->sa_family;
2945   switch (af)
2946   {
2947   case AF_INET:
2948     w_t4 = plugin->ipv4_addr_head;
2949     while (w_t4 != NULL)
2950     {
2951       int res = memcmp (&w_t4->addr->ipv4_addr,
2952                         &((struct sockaddr_in *) addr)->sin_addr,
2953                         sizeof (struct in_addr));
2954
2955       if (0 == res)
2956         break;
2957       w_t4 = w_t4->next;
2958     }
2959     if (w_t4 == NULL)
2960     {
2961       w_t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddressWrapper));
2962       t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddress));
2963       memcpy (&t4->ipv4_addr, &((struct sockaddr_in *) addr)->sin_addr,
2964               sizeof (struct in_addr));
2965       t4->port = htons (plugin->port_inbound);
2966
2967       w_t4->addr = t4;
2968
2969       GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head,
2970                                    plugin->ipv4_addr_tail, w_t4);
2971     }
2972     plugin->env->notify_address (plugin->env->cls, add_remove, w_t4->addr,
2973                                  sizeof (struct IPv4HttpAddress));
2974
2975     break;
2976   case AF_INET6:
2977     w_t6 = plugin->ipv6_addr_head;
2978     while (w_t6)
2979     {
2980       int res = memcmp (&w_t6->addr->ipv6_addr,
2981                         &((struct sockaddr_in6 *) addr)->sin6_addr,
2982                         sizeof (struct in6_addr));
2983
2984       if (0 == res)
2985         break;
2986       w_t6 = w_t6->next;
2987     }
2988     if (w_t6 == NULL)
2989     {
2990       w_t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddressWrapper));
2991       t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddress));
2992
2993       memcpy (&t6->ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
2994               sizeof (struct in6_addr));
2995       t6->port = htons (plugin->port_inbound);
2996
2997       w_t6->addr = t6;
2998
2999       GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head,
3000                                    plugin->ipv6_addr_tail, w_t6);
3001     }
3002     plugin->env->notify_address (plugin->env->cls, add_remove, w_t6->addr,
3003                                  sizeof (struct IPv6HttpAddress));
3004     break;
3005   default:
3006     return;
3007   }
3008
3009 }
3010
3011 static void
3012 tcp_nat_cb_remove_addr (void *cls, int add_remove, const struct sockaddr *addr,
3013                         socklen_t addrlen)
3014 {
3015   struct Plugin *plugin = cls;
3016   struct IPv4HttpAddressWrapper *w_t4 = NULL;
3017   struct IPv6HttpAddressWrapper *w_t6 = NULL;
3018   int af;
3019
3020   af = addr->sa_family;
3021   switch (af)
3022   {
3023   case AF_INET:
3024     w_t4 = plugin->ipv4_addr_head;
3025     while (w_t4 != NULL)
3026     {
3027       int res = memcmp (&w_t4->addr->ipv4_addr,
3028                         &((struct sockaddr_in *) addr)->sin_addr,
3029                         sizeof (struct in_addr));
3030
3031       if (0 == res)
3032         break;
3033       w_t4 = w_t4->next;
3034     }
3035     if (w_t4 == NULL)
3036       return;
3037     plugin->env->notify_address (plugin->env->cls, add_remove, w_t4->addr,
3038                                  sizeof (struct IPv4HttpAddress));
3039
3040     GNUNET_CONTAINER_DLL_remove (plugin->ipv4_addr_head, plugin->ipv4_addr_tail,
3041                                  w_t4);
3042     GNUNET_free (w_t4->addr);
3043     GNUNET_free (w_t4);
3044     break;
3045   case AF_INET6:
3046     w_t6 = plugin->ipv6_addr_head;
3047     while (w_t6 != NULL)
3048     {
3049       int res = memcmp (&w_t6->addr->ipv6_addr,
3050                         &((struct sockaddr_in6 *) addr)->sin6_addr,
3051                         sizeof (struct in6_addr));
3052
3053       if (0 == res)
3054         break;
3055       w_t6 = w_t6->next;
3056     }
3057     if (w_t6 == NULL)
3058       return;
3059     plugin->env->notify_address (plugin->env->cls, add_remove, w_t6->addr,
3060                                  sizeof (struct IPv6HttpAddress));
3061
3062     GNUNET_CONTAINER_DLL_remove (plugin->ipv6_addr_head, plugin->ipv6_addr_tail,
3063                                  w_t6);
3064     GNUNET_free (w_t6->addr);
3065     GNUNET_free (w_t6);
3066     break;
3067   default:
3068     return;
3069   }
3070
3071 }
3072
3073 /**
3074  * Our external IP address/port mapping has changed.
3075  *
3076  * @param cls closure, the 'struct LocalAddrList'
3077  * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
3078  *     the previous (now invalid) one
3079  * @param addr either the previous or the new public IP address
3080  * @param addrlen actual lenght of the address
3081  */
3082 static void
3083 tcp_nat_port_map_callback (void *cls, int add_remove,
3084                            const struct sockaddr *addr, socklen_t addrlen)
3085 {
3086   GNUNET_assert (cls != NULL);
3087 #if DEBUG_HTTP
3088   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NPMC called to %s address `%s'\n",
3089                    (add_remove == GNUNET_YES) ? "remove" : "add",
3090                    GNUNET_a2s (addr, addrlen));
3091 #endif
3092   /* convert 'addr' to our internal format */
3093   switch (add_remove)
3094   {
3095   case GNUNET_YES:
3096     tcp_nat_cb_add_addr (cls, add_remove, addr, addrlen);
3097     break;
3098   case GNUNET_NO:
3099     tcp_nat_cb_remove_addr (cls, add_remove, addr, addrlen);
3100     break;
3101   }
3102 }
3103
3104 /**
3105  * Exit point from the plugin.
3106  */
3107 void *
3108 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
3109 {
3110   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
3111   struct Plugin *plugin = api->cls;
3112   CURLMcode mret;
3113   struct IPv4HttpAddressWrapper *w_t4;
3114   struct IPv6HttpAddressWrapper *w_t6;
3115
3116   GNUNET_assert (cls != NULL);
3117
3118   if (plugin->nat != NULL)
3119     GNUNET_NAT_unregister (plugin->nat);
3120
3121   if (plugin->http_server_daemon_v4 != NULL)
3122   {
3123     MHD_stop_daemon (plugin->http_server_daemon_v4);
3124     plugin->http_server_daemon_v4 = NULL;
3125   }
3126   if (plugin->http_server_daemon_v6 != NULL)
3127   {
3128     MHD_stop_daemon (plugin->http_server_daemon_v6);
3129     plugin->http_server_daemon_v6 = NULL;
3130   }
3131   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
3132   {
3133     GNUNET_SCHEDULER_cancel (plugin->http_server_task_v4);
3134     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
3135   }
3136   if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
3137   {
3138     GNUNET_SCHEDULER_cancel (plugin->http_server_task_v6);
3139     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
3140   }
3141
3142   while (plugin->ipv4_addr_head != NULL)
3143   {
3144     w_t4 = plugin->ipv4_addr_head;
3145     GNUNET_CONTAINER_DLL_remove (plugin->ipv4_addr_head, plugin->ipv4_addr_tail,
3146                                  w_t4);
3147     GNUNET_free (w_t4->addr);
3148     GNUNET_free (w_t4);
3149   }
3150
3151   while (plugin->ipv6_addr_head != NULL)
3152   {
3153     w_t6 = plugin->ipv6_addr_head;
3154     GNUNET_CONTAINER_DLL_remove (plugin->ipv6_addr_head, plugin->ipv6_addr_tail,
3155                                  w_t6);
3156     GNUNET_free (w_t6->addr);
3157     GNUNET_free (w_t6);
3158   }
3159
3160   /* free all peer information */
3161   if (plugin->peers != NULL)
3162   {
3163     GNUNET_CONTAINER_multihashmap_iterate (plugin->peers,
3164                                            &remove_peer_context_Iterator,
3165                                            plugin);
3166     GNUNET_CONTAINER_multihashmap_destroy (plugin->peers);
3167   }
3168   if (plugin->multi_handle != NULL)
3169   {
3170     mret = curl_multi_cleanup (plugin->multi_handle);
3171     if (CURLM_OK != mret)
3172       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3173                   "curl multihandle clean up failed\n");
3174     plugin->multi_handle = NULL;
3175   }
3176   curl_global_cleanup ();
3177
3178   if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
3179   {
3180     GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
3181     plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
3182   }
3183
3184
3185   GNUNET_free_non_null (plugin->bind4_address);
3186   GNUNET_free_non_null (plugin->bind6_address);
3187   GNUNET_free_non_null (plugin->bind_hostname);
3188 #if BUILD_HTTPS
3189   GNUNET_free_non_null (plugin->crypto_init);
3190   GNUNET_free_non_null (plugin->cert);
3191   GNUNET_free_non_null (plugin->key);
3192 #endif
3193   GNUNET_free (plugin);
3194   GNUNET_free (api);
3195 #if DEBUG_HTTP
3196   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Unload %s plugin complete...\n",
3197               PROTOCOL_PREFIX);
3198 #endif
3199   return NULL;
3200 }
3201
3202 #if BUILD_HTTPS
3203 static char *
3204 load_certificate (const char *file)
3205 {
3206   struct GNUNET_DISK_FileHandle *gn_file;
3207   struct stat fstat;
3208   char *text = NULL;
3209
3210   if (0 != STAT (file, &fstat))
3211     return NULL;
3212   text = GNUNET_malloc (fstat.st_size + 1);
3213   gn_file =
3214       GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
3215                              GNUNET_DISK_PERM_USER_READ);
3216   if (gn_file == NULL)
3217   {
3218     GNUNET_free (text);
3219     return NULL;
3220   }
3221   if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fstat.st_size))
3222   {
3223     GNUNET_free (text);
3224     GNUNET_DISK_file_close (gn_file);
3225     return NULL;
3226   }
3227   text[fstat.st_size] = '\0';
3228   GNUNET_DISK_file_close (gn_file);
3229   return text;
3230 }
3231 #endif
3232
3233
3234 /**
3235  * Entry point for the plugin.
3236  */
3237 void *
3238 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
3239 {
3240   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
3241   struct Plugin *plugin;
3242   struct GNUNET_TRANSPORT_PluginFunctions *api;
3243   struct GNUNET_TIME_Relative gn_timeout;
3244   long long unsigned int port;
3245   unsigned long long tneigh;
3246   struct sockaddr **addrs;
3247   socklen_t *addrlens;
3248   int ret;
3249   char *component_name;
3250
3251 #if BUILD_HTTPS
3252   char *key_file = NULL;
3253   char *cert_file = NULL;
3254 #endif
3255
3256   GNUNET_assert (cls != NULL);
3257 #if DEBUG_HTTP
3258   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting %s plugin...\n",
3259               PROTOCOL_PREFIX);
3260 #endif
3261   GNUNET_asprintf (&component_name, "transport-%s", PROTOCOL_PREFIX);
3262
3263   plugin = GNUNET_malloc (sizeof (struct Plugin));
3264   plugin->stats = env->stats;
3265   plugin->env = env;
3266   plugin->peers = NULL;
3267   plugin->bind4_address = NULL;
3268   plugin->bind6_address = NULL;
3269   plugin->use_ipv6 = GNUNET_YES;
3270   plugin->use_ipv4 = GNUNET_YES;
3271   plugin->use_localaddresses = GNUNET_NO;
3272
3273   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
3274   api->cls = plugin;
3275   api->send = &http_plugin_send;
3276   api->disconnect = &http_plugin_disconnect;
3277   api->address_pretty_printer = &http_plugin_address_pretty_printer;
3278   api->check_address = &http_plugin_address_suggested;
3279   api->address_to_string = &http_plugin_address_to_string;
3280
3281   /* Hashing our identity to use it in URLs */
3282   GNUNET_CRYPTO_hash_to_enc (&(plugin->env->my_identity->hashPubKey),
3283                              &plugin->my_ascii_hash_ident);
3284
3285
3286   if (GNUNET_OK !=
3287       GNUNET_CONFIGURATION_get_value_number (env->cfg, component_name,
3288                                              "MAX_CONNECTIONS", &tneigh))
3289     tneigh = 128;
3290   plugin->max_connect_per_transport = tneigh;
3291
3292
3293   /* Use IPv6? */
3294   if (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "USE_IPv6"))
3295   {
3296     plugin->use_ipv6 =
3297         GNUNET_CONFIGURATION_get_value_yesno (env->cfg, component_name,
3298                                               "USE_IPv6");
3299   }
3300   /* Use IPv4? */
3301   if (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "USE_IPv4"))
3302   {
3303     plugin->use_ipv4 =
3304         GNUNET_CONFIGURATION_get_value_yesno (env->cfg, component_name,
3305                                               "USE_IPv4");
3306   }
3307   /* use local addresses? */
3308
3309   if (GNUNET_CONFIGURATION_have_value
3310       (env->cfg, component_name, "USE_LOCALADDR"))
3311   {
3312     plugin->use_localaddresses =
3313         GNUNET_CONFIGURATION_get_value_yesno (env->cfg, component_name,
3314                                               "USE_LOCALADDR");
3315   }
3316   /* Reading port number from config file */
3317   if ((GNUNET_OK !=
3318        GNUNET_CONFIGURATION_get_value_number (env->cfg, component_name, "PORT",
3319                                               &port)) || (port > 65535))
3320   {
3321     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, component_name,
3322                      _
3323                      ("Require valid port number for transport plugin `%s' in configuration!\n"),
3324                      PROTOCOL_PREFIX);
3325     GNUNET_free (component_name);
3326     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3327     return NULL;
3328   }
3329
3330   /* Reading ipv4 addresse to bind to from config file */
3331   if ((plugin->use_ipv4 == GNUNET_YES) &&
3332       (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "BINDTO4")))
3333   {
3334     GNUNET_break (GNUNET_OK ==
3335                   GNUNET_CONFIGURATION_get_value_string (env->cfg,
3336                                                          component_name,
3337                                                          "BINDTO4",
3338                                                          &plugin->bind_hostname));
3339     plugin->bind4_address = GNUNET_malloc (sizeof (struct sockaddr_in));
3340     plugin->bind4_address->sin_family = AF_INET;
3341     plugin->bind4_address->sin_port = htons (port);
3342
3343     if (plugin->bind_hostname != NULL)
3344     {
3345       if (inet_pton
3346           (AF_INET, plugin->bind_hostname,
3347            &plugin->bind4_address->sin_addr) <= 0)
3348       {
3349         GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, component_name,
3350                          _
3351                          ("Misconfigured address to bind to in configuration!\n"));
3352         GNUNET_free (plugin->bind4_address);
3353         GNUNET_free (plugin->bind_hostname);
3354         plugin->bind_hostname = NULL;
3355         plugin->bind4_address = NULL;
3356       }
3357     }
3358   }
3359
3360   /* Reading ipv4 addresse to bind to from config file */
3361   if ((plugin->use_ipv6 == GNUNET_YES) &&
3362       (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "BINDTO6")))
3363   {
3364     if (GNUNET_OK ==
3365         GNUNET_CONFIGURATION_get_value_string (env->cfg, component_name,
3366                                                "BINDTO6",
3367                                                &plugin->bind_hostname))
3368     {
3369       plugin->bind6_address = GNUNET_malloc (sizeof (struct sockaddr_in6));
3370       plugin->bind6_address->sin6_family = AF_INET6;
3371       plugin->bind6_address->sin6_port = htons (port);
3372       if (plugin->bind_hostname != NULL)
3373       {
3374         if (inet_pton
3375             (AF_INET6, plugin->bind_hostname,
3376              &plugin->bind6_address->sin6_addr) <= 0)
3377         {
3378           GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, component_name,
3379                            _
3380                            ("Misconfigured address to bind to in configuration!\n"));
3381           GNUNET_free (plugin->bind6_address);
3382           GNUNET_free (plugin->bind_hostname);
3383           plugin->bind_hostname = NULL;
3384           plugin->bind6_address = NULL;
3385         }
3386       }
3387     }
3388   }
3389
3390 #if BUILD_HTTPS
3391   /* Reading HTTPS crypto related configuration */
3392   /* Get crypto init string from config */
3393   if ((GNUNET_OK !=
3394        GNUNET_CONFIGURATION_get_value_string (env->cfg, "transport-https",
3395                                               "CRYPTO_INIT",
3396                                               &plugin->crypto_init)) ||
3397       (GNUNET_OK !=
3398        GNUNET_CONFIGURATION_get_value_filename (env->cfg, "transport-https",
3399                                                 "KEY_FILE", &key_file)) ||
3400       (GNUNET_OK !=
3401        GNUNET_CONFIGURATION_get_value_filename (env->cfg, "transport-https",
3402                                                 "CERT_FILE", &cert_file)))
3403   {
3404     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "https",
3405                      _
3406                      ("Required configuration options missing in section `%s'\n"),
3407                      "transport-https");
3408     GNUNET_free (component_name);
3409     GNUNET_free_non_null (key_file);
3410     GNUNET_free_non_null (cert_file);
3411     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3412     return NULL;
3413   }
3414
3415   /* read key & certificates from file */
3416   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading TLS certificate `%s' `%s'\n",
3417               key_file, cert_file);
3418
3419   plugin->key = load_certificate (key_file);
3420   plugin->cert = load_certificate (cert_file);
3421
3422   if ((plugin->key == NULL) || (plugin->cert == NULL))
3423   {
3424     struct GNUNET_OS_Process *certcreation;
3425
3426     GNUNET_free_non_null (plugin->key);
3427     plugin->key = NULL;
3428     GNUNET_free_non_null (plugin->cert);
3429     plugin->cert = NULL;
3430 #if DEBUG_HTTP
3431     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3432                 "No usable TLS certificate found, creating certificate\n");
3433 #endif
3434     errno = 0;
3435     certcreation =
3436         GNUNET_OS_start_process (NULL, NULL,
3437                                  "gnunet-transport-certificate-creation",
3438                                  "gnunet-transport-certificate-creation",
3439                                  key_file, cert_file, NULL);
3440     if (certcreation == NULL)
3441     {
3442       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "https",
3443                        _
3444                        ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
3445       GNUNET_free (key_file);
3446       GNUNET_free (cert_file);
3447       GNUNET_free (component_name);
3448       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3449       return NULL;
3450     }
3451     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (certcreation));
3452     GNUNET_OS_process_close (certcreation);
3453     plugin->key = load_certificate (key_file);
3454     plugin->cert = load_certificate (cert_file);
3455   }
3456   if ((plugin->key == NULL) || (plugin->cert == NULL))
3457   {
3458     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "https",
3459                      _
3460                      ("No usable TLS certificate found and creating one failed!\n"),
3461                      "transport-https");
3462     GNUNET_free (key_file);
3463     GNUNET_free (cert_file);
3464     GNUNET_free (component_name);
3465     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3466     return NULL;
3467   }
3468   GNUNET_free (key_file);
3469   GNUNET_free (cert_file);
3470 #if DEBUG_HTTP
3471   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
3472 #endif
3473 #endif
3474
3475   GNUNET_assert ((port > 0) && (port <= 65535));
3476   plugin->port_inbound = port;
3477   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
3478   unsigned int timeout = (gn_timeout.rel_value) / 1000;
3479
3480   if ((plugin->http_server_daemon_v6 == NULL) &&
3481       (plugin->use_ipv6 == GNUNET_YES) && (port != 0))
3482   {
3483     struct sockaddr *tmp = (struct sockaddr *) plugin->bind6_address;
3484
3485     plugin->http_server_daemon_v6 = MHD_start_daemon (
3486 #if DEBUG_MHD
3487                                                        MHD_USE_DEBUG |
3488 #endif
3489 #if BUILD_HTTPS
3490                                                        MHD_USE_SSL |
3491 #endif
3492                                                        MHD_USE_IPv6, port,
3493                                                        &mhd_accept_cb, plugin,
3494                                                        &mhd_access_cb, plugin,
3495                                                        MHD_OPTION_SOCK_ADDR,
3496                                                        tmp,
3497                                                        MHD_OPTION_CONNECTION_LIMIT,
3498                                                        (unsigned int)
3499                                                        plugin->max_connect_per_transport,
3500 #if BUILD_HTTPS
3501                                                        MHD_OPTION_HTTPS_PRIORITIES,
3502                                                        plugin->crypto_init,
3503                                                        MHD_OPTION_HTTPS_MEM_KEY,
3504                                                        plugin->key,
3505                                                        MHD_OPTION_HTTPS_MEM_CERT,
3506                                                        plugin->cert,
3507 #endif
3508                                                        MHD_OPTION_CONNECTION_TIMEOUT,
3509                                                        (unsigned int) timeout,
3510                                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
3511                                                        (size_t) (2 *
3512                                                                  GNUNET_SERVER_MAX_MESSAGE_SIZE),
3513                                                        MHD_OPTION_NOTIFY_COMPLETED,
3514                                                        &mhd_termination_cb,
3515                                                        plugin,
3516                                                        MHD_OPTION_EXTERNAL_LOGGER,
3517                                                        mhd_logger,
3518                                                        plugin->mhd_log,
3519                                                        MHD_OPTION_END);
3520   }
3521   if ((plugin->http_server_daemon_v4 == NULL) &&
3522       (plugin->use_ipv4 == GNUNET_YES) && (port != 0))
3523   {
3524     plugin->http_server_daemon_v4 = MHD_start_daemon (
3525 #if DEBUG_MHD
3526                                                        MHD_USE_DEBUG |
3527 #endif
3528 #if BUILD_HTTPS
3529                                                        MHD_USE_SSL |
3530 #endif
3531                                                        MHD_NO_FLAG, port,
3532                                                        &mhd_accept_cb, plugin,
3533                                                        &mhd_access_cb, plugin,
3534                                                        MHD_OPTION_SOCK_ADDR,
3535                                                        (struct sockaddr_in *)
3536                                                        plugin->bind4_address,
3537                                                        MHD_OPTION_CONNECTION_LIMIT,
3538                                                        (unsigned int)
3539                                                        plugin->max_connect_per_transport,
3540 #if BUILD_HTTPS
3541                                                        MHD_OPTION_HTTPS_PRIORITIES,
3542                                                        plugin->crypto_init,
3543                                                        MHD_OPTION_HTTPS_MEM_KEY,
3544                                                        plugin->key,
3545                                                        MHD_OPTION_HTTPS_MEM_CERT,
3546                                                        plugin->cert,
3547 #endif
3548                                                        MHD_OPTION_CONNECTION_TIMEOUT,
3549                                                        (unsigned int) timeout,
3550                                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
3551                                                        (size_t) (2 *
3552                                                                  GNUNET_SERVER_MAX_MESSAGE_SIZE),
3553                                                        MHD_OPTION_NOTIFY_COMPLETED,
3554                                                        &mhd_termination_cb,
3555                                                        plugin,
3556                                                        MHD_OPTION_EXTERNAL_LOGGER,
3557                                                        mhd_logger,
3558                                                        plugin->mhd_log,
3559                                                        MHD_OPTION_END);
3560   }
3561   if (plugin->http_server_daemon_v4 != NULL)
3562     plugin->http_server_task_v4 =
3563         http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
3564   if (plugin->http_server_daemon_v6 != NULL)
3565     plugin->http_server_task_v6 =
3566         http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
3567
3568
3569   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
3570   {
3571 #if DEBUG_HTTP
3572     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3573                 "Starting MHD with IPv4 bound to %s with port %u\n",
3574                 (plugin->bind_hostname !=
3575                  NULL) ? plugin->bind_hostname : "every address", port);
3576 #endif
3577   }
3578   else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) &&
3579            (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK))
3580   {
3581 #if DEBUG_HTTP
3582     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3583                 "Starting MHD with IPv6 bound to %s with port %u\n",
3584                 (plugin->bind_hostname !=
3585                  NULL) ? plugin->bind_hostname : "every address", port);
3586 #endif
3587   }
3588   else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) &&
3589            (plugin->http_server_task_v4 == GNUNET_SCHEDULER_NO_TASK))
3590   {
3591 #if DEBUG_HTTP
3592     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3593                 "Starting MHD with IPv4 and IPv6 bound to %s with port %u\n",
3594                 (plugin->bind_hostname !=
3595                  NULL) ? plugin->bind_hostname : "every address", port);
3596 #endif
3597   }
3598   else
3599   {
3600     char *tmp = NULL;
3601
3602     if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_YES))
3603       GNUNET_asprintf (&tmp, "with IPv4 and IPv6 enabled");
3604     if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_YES))
3605       GNUNET_asprintf (&tmp, "with IPv4 enabled");
3606     if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_NO))
3607       GNUNET_asprintf (&tmp, "with IPv6 enabled");
3608     if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_NO))
3609       GNUNET_asprintf (&tmp, "with NO IP PROTOCOL enabled");
3610     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3611                 _
3612                 ("HTTP Server with %s could not be started on port %u! %s plugin failed!\n"),
3613                 tmp, port, PROTOCOL_PREFIX);
3614     GNUNET_free (tmp);
3615     GNUNET_free (component_name);
3616     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3617     return NULL;
3618   }
3619
3620   /* Initializing cURL */
3621   curl_global_init (CURL_GLOBAL_ALL);
3622   plugin->multi_handle = curl_multi_init ();
3623
3624   if (NULL == plugin->multi_handle)
3625   {
3626     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, component_name,
3627                      _
3628                      ("Could not initialize curl multi handle, failed to start %s plugin!\n"),
3629                      PROTOCOL_PREFIX);
3630     GNUNET_free (component_name);
3631     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3632     return NULL;
3633   }
3634
3635   ret =
3636       GNUNET_SERVICE_get_server_addresses (component_name, env->cfg, &addrs,
3637                                            &addrlens);
3638
3639   if (ret != GNUNET_SYSERR)
3640   {
3641     plugin->nat =
3642         GNUNET_NAT_register (env->cfg, GNUNET_YES, port, (unsigned int) ret,
3643                              (const struct sockaddr **) addrs, addrlens,
3644                              &tcp_nat_port_map_callback,
3645                              &try_connection_reversal, plugin);
3646     while (ret > 0)
3647     {
3648       ret--;
3649       GNUNET_assert (addrs[ret] != NULL);
3650       GNUNET_free (addrs[ret]);
3651     }
3652     GNUNET_free_non_null (addrs);
3653     GNUNET_free_non_null (addrlens);
3654   }
3655   else
3656   {
3657     plugin->nat =
3658         GNUNET_NAT_register (env->cfg, GNUNET_YES, 0, 0, NULL, NULL, NULL,
3659                              &try_connection_reversal, plugin);
3660   }
3661
3662   plugin->peers = GNUNET_CONTAINER_multihashmap_create (10);
3663
3664   GNUNET_free (component_name);
3665   //GNUNET_SCHEDULER_add_now(address_notification, plugin);
3666   return api;
3667 }
3668
3669 /* end of plugin_transport_http.c */