indentation
[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 *http_plugin_address_to_string (void *cls, const void *addr,
551                                                   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 http_server_daemon_v4_run (void *cls,
559                                        const struct GNUNET_SCHEDULER_TaskContext
560                                        *tc);
561 /**
562  * Call MHD to process pending ipv6 requests and then go back
563  * and schedule the next run.
564  */
565 static void http_server_daemon_v6_run (void *cls,
566                                        const struct GNUNET_SCHEDULER_TaskContext
567                                        *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 send_check_connections (struct Plugin *plugin, struct Session *ps);
577
578 /**
579  * Function setting up file descriptors and scheduling task to run
580  *
581  * @param  plugin plugin as closure
582  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
583  */
584 static int curl_schedule (struct Plugin *plugin);
585
586 /**
587  * Task scheduled to reset the inbound quota delay for a specific peer
588  * @param cls plugin as closure
589  * @param tc task context
590  */
591 static void
592 reset_inbound_quota_delay (void *cls,
593                            const struct GNUNET_SCHEDULER_TaskContext *tc)
594 {
595   struct HTTP_PeerContext *pc = cls;
596
597   GNUNET_assert (cls != NULL);
598   pc->reset_task = GNUNET_SCHEDULER_NO_TASK;
599   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
600     return;
601   pc->delay = GNUNET_TIME_relative_get_zero ();
602 }
603
604
605 /**
606  * Creates a valid url from passed address and id
607  * @param plugin plugin
608  * @param addr address to create url from
609  * @param addrlen address lenth
610  * @param id session id
611  * @return the created url
612  */
613 static char *
614 create_url (struct Plugin *plugin, const void *addr, size_t addrlen, size_t id)
615 {
616   char *url = NULL;
617   char *addr_str = (char *) http_plugin_address_to_string (NULL, addr, addrlen);
618
619   GNUNET_assert ((addr != NULL) && (addrlen != 0));
620   GNUNET_asprintf (&url, "%s://%s/%s;%u", PROTOCOL_PREFIX, addr_str,
621                    (char *) (&plugin->my_ascii_hash_ident), id);
622   return url;
623 }
624
625
626 /**
627  * Removes a message from the linked list of messages
628  * @param ps session
629  * @param msg message
630  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
631  */
632 static int
633 remove_http_message (struct Session *ps, struct HTTP_Message *msg)
634 {
635   GNUNET_CONTAINER_DLL_remove (ps->pending_msgs_head, ps->pending_msgs_tail,
636                                msg);
637   GNUNET_free (msg);
638   return GNUNET_OK;
639 }
640
641 /**
642  * Iterator to remove peer context
643  * @param cls the plugin
644  * @param key the peers public key hashcode
645  * @param value the peer context
646  * @return GNUNET_YES on success
647  */
648 static int
649 remove_peer_context_Iterator (void *cls, const GNUNET_HashCode * key,
650                               void *value)
651 {
652   struct Plugin *plugin = cls;
653   struct HTTP_PeerContext *pc = value;
654   struct Session *ps = pc->head;
655   struct Session *tmp = NULL;
656   struct HTTP_Message *msg = NULL;
657   struct HTTP_Message *msg_tmp = NULL;
658
659 #if DEBUG_HTTP
660   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Freeing context for peer `%s'\n",
661               GNUNET_i2s (&pc->identity));
662 #endif
663   GNUNET_assert (GNUNET_YES ==
664                  GNUNET_CONTAINER_multihashmap_remove (plugin->peers,
665                                                        &pc->identity.hashPubKey,
666                                                        pc));
667   while (ps != NULL)
668   {
669     plugin->env->session_end (plugin, &pc->identity, ps);
670     tmp = ps->next;
671
672     GNUNET_free_non_null (ps->addr);
673     GNUNET_free (ps->url);
674     if (ps->msgtok != NULL)
675       GNUNET_SERVER_mst_destroy (ps->msgtok);
676
677     msg = ps->pending_msgs_head;
678     while (msg != NULL)
679     {
680       msg_tmp = msg->next;
681       GNUNET_free (msg);
682       msg = msg_tmp;
683     }
684     if (ps->direction == OUTBOUND)
685     {
686       if (ps->send_endpoint != NULL)
687         curl_easy_cleanup (ps->send_endpoint);
688       if (ps->recv_endpoint != NULL)
689         curl_easy_cleanup (ps->recv_endpoint);
690     }
691     GNUNET_free (ps);
692     ps = tmp;
693   }
694   GNUNET_free (pc);
695   GNUNET_STATISTICS_update (plugin->env->stats,
696                             gettext_noop ("# HTTP peers active"), -1,
697                             GNUNET_NO);
698   return GNUNET_YES;
699 }
700
701
702 /**
703  * Removes a session from the linked list of sessions
704  * @param pc peer context
705  * @param ps session
706  * @param call_msg_cont GNUNET_YES to call pending message continuations, otherwise no
707  * @param call_msg_cont_result result to call message continuations with
708  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
709  */
710 static int
711 remove_session (struct HTTP_PeerContext *pc, struct Session *ps,
712                 int call_msg_cont, int call_msg_cont_result)
713 {
714   struct HTTP_Message *msg;
715   struct Plugin *plugin = ps->peercontext->plugin;
716
717 #if DEBUG_CONNECTIONS
718   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
719               "Connection %X: removing %s session %X with id %u\n", ps,
720               (ps->direction == INBOUND) ? "inbound" : "outbound", ps,
721               ps->session_id);
722 #endif
723   plugin->env->session_end (plugin, &pc->identity, ps);
724   GNUNET_free_non_null (ps->addr);
725   GNUNET_SERVER_mst_destroy (ps->msgtok);
726   GNUNET_free (ps->url);
727   if (ps->direction == INBOUND)
728   {
729     if (ps->recv_endpoint != NULL)
730     {
731       curl_easy_cleanup (ps->recv_endpoint);
732       ps->recv_endpoint = NULL;
733     }
734     if (ps->send_endpoint != NULL)
735     {
736       curl_easy_cleanup (ps->send_endpoint);
737       ps->send_endpoint = NULL;
738     }
739   }
740
741   msg = ps->pending_msgs_head;
742   while (msg != NULL)
743   {
744     if ((call_msg_cont == GNUNET_YES) && (msg->transmit_cont != NULL))
745     {
746       msg->transmit_cont (msg->transmit_cont_cls, &pc->identity,
747                           call_msg_cont_result);
748     }
749     GNUNET_CONTAINER_DLL_remove (ps->pending_msgs_head, ps->pending_msgs_head,
750                                  msg);
751     GNUNET_free (msg);
752     msg = ps->pending_msgs_head;
753   }
754
755   GNUNET_CONTAINER_DLL_remove (pc->head, pc->tail, ps);
756   GNUNET_free (ps);
757   ps = NULL;
758
759   /* no sessions left remove peer */
760   if (pc->head == NULL)
761   {
762 #if DEBUG_HTTP
763     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
764                 "No sessions left for peer `%s', removing context\n",
765                 GNUNET_i2s (&pc->identity));
766 #endif
767     remove_peer_context_Iterator (plugin, &pc->identity.hashPubKey, pc);
768   }
769
770   return GNUNET_OK;
771 }
772
773
774 #if 0
775 static int
776 check_localaddress (const struct sockaddr *addr, socklen_t addrlen)
777 {
778   uint32_t res = 0;
779   int local = GNUNET_NO;
780   int af = addr->sa_family;
781
782   switch (af)
783   {
784   case AF_INET:
785   {
786     uint32_t netmask = 0x7F000000;
787     uint32_t address = ntohl (((struct sockaddr_in *) addr)->sin_addr.s_addr);
788
789     res = (address >> 24) ^ (netmask >> 24);
790     if (res != 0)
791       local = GNUNET_NO;
792     else
793       local = GNUNET_YES;
794 #if DEBUG_HTTP
795     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking IPv4 address `%s': %s\n",
796                 GNUNET_a2s (addr, addrlen),
797                 (local == GNUNET_YES) ? "local" : "global");
798 #endif
799     break;
800   }
801   case AF_INET6:
802   {
803     if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr) ||
804         IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
805       local = GNUNET_YES;
806     else
807       local = GNUNET_NO;
808 #if DEBUG_HTTP
809     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking IPv6 address `%s' : %s\n",
810                 GNUNET_a2s (addr, addrlen),
811                 (local == GNUNET_YES) ? "local" : "global");
812 #endif
813     break;
814   }
815   }
816   return local;
817 }
818
819
820 /**
821  * Add the IP of our network interface to the list of
822  * our external IP addresses.
823  *
824  * @param cls the 'struct Plugin*'
825  * @param name name of the interface
826  * @param isDefault do we think this may be our default interface
827  * @param addr address of the interface
828  * @param addrlen number of bytes in addr
829  * @return GNUNET_OK to continue iterating
830  */
831 static int
832 process_interfaces (void *cls, const char *name, int isDefault,
833                     const struct sockaddr *addr, socklen_t addrlen)
834 {
835   struct Plugin *plugin = cls;
836   struct IPv4HttpAddress *t4;
837   struct IPv6HttpAddress *t6;
838   int af;
839
840   if (plugin->use_localaddresses == GNUNET_NO)
841   {
842     if (GNUNET_YES == check_localaddress (addr, addrlen))
843     {
844 #if DEBUG_HTTP
845       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, PROTOCOL_PREFIX,
846                        "Not notifying transport of address `%s' (local address)\n",
847                        GNUNET_a2s (addr, addrlen));
848 #endif
849       return GNUNET_OK;
850     }
851   }
852
853
854   GNUNET_assert (cls != NULL);
855   af = addr->sa_family;
856   if ((af == AF_INET) && (plugin->use_ipv4 == GNUNET_YES) &&
857       (plugin->bind6_address == NULL))
858   {
859
860     struct in_addr bnd_cmp = ((struct sockaddr_in *) addr)->sin_addr;
861
862     t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddress));
863     // Not skipping loopback addresses
864
865
866     t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
867     t4->port = htons (plugin->port_inbound);
868     if (plugin->bind4_address != NULL)
869     {
870       if (0 ==
871           memcmp (&plugin->bind4_address->sin_addr, &bnd_cmp,
872                   sizeof (struct in_addr)))
873       {
874         GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head,
875                                      plugin->ipv4_addr_tail, t4);
876         plugin->env->notify_address (plugin->env->cls, GNUNET_YES, t4,
877                                      sizeof (struct IPv4HttpAddress));
878         return GNUNET_OK;
879       }
880       GNUNET_free (t4);
881       return GNUNET_OK;
882     }
883     else
884     {
885       GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head,
886                                    plugin->ipv4_addr_tail, t4);
887       plugin->env->notify_address (plugin->env->cls, GNUNET_YES, t4,
888                                    sizeof (struct IPv4HttpAddress));
889       return GNUNET_OK;
890     }
891   }
892   if ((af == AF_INET6) && (plugin->use_ipv6 == GNUNET_YES) &&
893       (plugin->bind4_address == NULL))
894   {
895
896     struct in6_addr bnd_cmp6 = ((struct sockaddr_in6 *) addr)->sin6_addr;
897
898     t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddress));
899     GNUNET_assert (t6 != NULL);
900
901     if (plugin->bind6_address != NULL)
902     {
903       if (0 ==
904           memcmp (&plugin->bind6_address->sin6_addr, &bnd_cmp6,
905                   sizeof (struct in6_addr)))
906       {
907         memcpy (&t6->ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
908                 sizeof (struct in6_addr));
909         t6->port = htons (plugin->port_inbound);
910         plugin->env->notify_address (plugin->env->cls, GNUNET_YES, t6,
911                                      sizeof (struct IPv6HttpAddress));
912         GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head,
913                                      plugin->ipv6_addr_tail, t6);
914         return GNUNET_OK;
915       }
916       GNUNET_free (t6);
917       return GNUNET_OK;
918     }
919     memcpy (&t6->ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
920             sizeof (struct in6_addr));
921     t6->port = htons (plugin->port_inbound);
922     GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head, plugin->ipv6_addr_tail,
923                                  t6);
924
925     plugin->env->notify_address (plugin->env->cls, GNUNET_YES, t6,
926                                  sizeof (struct IPv6HttpAddress));
927   }
928   return GNUNET_OK;
929 }
930 #endif
931
932 /**
933  * External logging function for MHD
934  * @param arg arguments
935  * @param fmt format string
936  * @param ap  list of arguments
937  */
938 static void
939 mhd_logger (void *arg, const char *fmt, va_list ap)
940 {
941   char text[1024];
942
943   vsnprintf (text, sizeof (text), fmt, ap);
944   va_end (ap);
945   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MHD: %s\n", text);
946 }
947
948
949 static void
950 mhd_termination_cb (void *cls, struct MHD_Connection *connection,
951                     void **httpSessionCache)
952 {
953   struct Session *ps = *httpSessionCache;
954
955   if (ps == NULL)
956     return;
957   struct HTTP_PeerContext *pc = ps->peercontext;
958   struct Plugin *plugin = cls;
959
960   GNUNET_assert (cls != NULL);
961   plugin->current_connections--;
962
963   if (connection == ps->recv_endpoint)
964   {
965 #if DEBUG_CONNECTIONS
966     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
967                 "Connection %X: inbound connection from peer `%s' was terminated\n",
968                 ps, GNUNET_i2s (&pc->identity));
969 #endif
970     ps->recv_active = GNUNET_NO;
971     ps->recv_connected = GNUNET_NO;
972     ps->recv_endpoint = NULL;
973   }
974   if (connection == ps->send_endpoint)
975   {
976     ps->send_active = GNUNET_NO;
977     ps->send_connected = GNUNET_NO;
978     ps->send_endpoint = NULL;
979 #if DEBUG_CONNECTIONS
980     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
981                 "Connection %X: outbound connection from peer `%s' was terminated\n",
982                 ps, GNUNET_i2s (&pc->identity));
983 #endif
984   }
985
986   /* if both connections disconnected, remove session */
987   if ((ps->send_connected == GNUNET_NO) && (ps->recv_connected == GNUNET_NO))
988   {
989     GNUNET_STATISTICS_update (pc->plugin->env->stats,
990                               gettext_noop
991                               ("# HTTP inbound sessions for peers active"), -1,
992                               GNUNET_NO);
993     remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR);
994   }
995 }
996
997
998 /**
999  * Callback called by MessageStreamTokenizer when a message has arrived
1000  * @param cls current session as closure
1001  * @param client clien
1002  * @param message the message to be forwarded to transport service
1003  */
1004 static void
1005 mhd_write_mst_cb (void *cls, void *client,
1006                   const struct GNUNET_MessageHeader *message)
1007 {
1008   struct Session *ps = cls;
1009   struct HTTP_PeerContext *pc;
1010   struct GNUNET_TIME_Relative delay;
1011
1012   GNUNET_assert (ps != NULL);
1013   pc = ps->peercontext;
1014   GNUNET_assert (pc != NULL);
1015 #if DEBUG_HTTP
1016   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1017               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
1018               ps, ntohs (message->type), ntohs (message->size),
1019               GNUNET_i2s (&(ps->peercontext)->identity),
1020               http_plugin_address_to_string (NULL, ps->addr, ps->addrlen));
1021 #endif
1022   struct GNUNET_TRANSPORT_ATS_Information distance[2];
1023
1024   distance[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
1025   distance[0].value = htonl (1);
1026   distance[1].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
1027   distance[1].value = htonl (0);
1028   delay =
1029       pc->plugin->env->receive (ps->peercontext->plugin->env->cls,
1030                                 &pc->identity, message,
1031                                 (const struct GNUNET_TRANSPORT_ATS_Information
1032                                  *) &distance, 2, ps, NULL, 0);
1033   pc->delay = delay;
1034   if (pc->reset_task != GNUNET_SCHEDULER_NO_TASK)
1035     GNUNET_SCHEDULER_cancel (pc->reset_task);
1036
1037   if (delay.rel_value > 0)
1038   {
1039 #if DEBUG_HTTP
1040     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1041                 "Connection %X: Inbound quota management: delay next read for %llu ms \n",
1042                 ps, delay.rel_value);
1043 #endif
1044     pc->reset_task =
1045         GNUNET_SCHEDULER_add_delayed (delay, &reset_inbound_quota_delay, pc);
1046   }
1047 }
1048
1049
1050 /**
1051  * Check if incoming connection is accepted.
1052  * NOTE: Here every connection is accepted
1053  * @param cls plugin as closure
1054  * @param addr address of incoming connection
1055  * @param addr_len address length of incoming connection
1056  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
1057  *
1058  */
1059 static int
1060 mhd_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
1061 {
1062   struct Plugin *plugin = cls;
1063
1064   GNUNET_assert (cls != NULL);
1065
1066   if (plugin->max_connect_per_transport > plugin->current_connections)
1067   {
1068     plugin->current_connections++;
1069     return MHD_YES;
1070   }
1071   else
1072     return MHD_NO;
1073 }
1074
1075
1076 /**
1077  * Callback called by MHD when it needs data to send
1078  * @param cls current session
1079  * @param pos position in buffer
1080  * @param buf the buffer to write data to
1081  * @param max max number of bytes available in buffer
1082  * @return bytes written to buffer
1083  */
1084 static ssize_t
1085 mhd_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
1086 {
1087   struct Session *ps = cls;
1088   struct HTTP_PeerContext *pc;
1089   struct HTTP_Message *msg;
1090   int bytes_read = 0;
1091
1092   GNUNET_assert (ps != NULL);
1093
1094   pc = ps->peercontext;
1095   msg = ps->pending_msgs_tail;
1096   if (ps->send_force_disconnect == GNUNET_YES)
1097   {
1098 #if DEBUG_CONNECTIONS
1099     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1100                 "Connection %X: outbound forced to disconnect\n", ps);
1101 #endif
1102     return -1;
1103   }
1104
1105   if (msg != NULL)
1106   {
1107     /* sending */
1108     if ((msg->size - msg->pos) <= max)
1109     {
1110       memcpy (buf, &msg->buf[msg->pos], (msg->size - msg->pos));
1111       bytes_read = msg->size - msg->pos;
1112       msg->pos += (msg->size - msg->pos);
1113     }
1114     else
1115     {
1116       memcpy (buf, &msg->buf[msg->pos], max);
1117       msg->pos += max;
1118       bytes_read = max;
1119     }
1120
1121     /* removing message */
1122     if (msg->pos == msg->size)
1123     {
1124       if (NULL != msg->transmit_cont)
1125         msg->transmit_cont (msg->transmit_cont_cls, &pc->identity, GNUNET_OK);
1126       ps->queue_length_cur -= msg->size;
1127       remove_http_message (ps, msg);
1128     }
1129   }
1130 #if DEBUG_CONNECTIONS
1131   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection %X: MHD has sent %u bytes\n",
1132               ps, bytes_read);
1133 #endif
1134   return bytes_read;
1135 }
1136
1137
1138 /**
1139  * Process GET or PUT request received via MHD.  For
1140  * GET, queue response that will send back our pending
1141  * messages.  For PUT, process incoming data and send
1142  * to GNUnet core.  In either case, check if a session
1143  * already exists and create a new one if not.
1144  */
1145 static int
1146 mhd_access_cb (void *cls, struct MHD_Connection *mhd_connection,
1147                const char *url, const char *method, const char *version,
1148                const char *upload_data, size_t * upload_data_size,
1149                void **httpSessionCache)
1150 {
1151   struct Plugin *plugin = cls;
1152   struct MHD_Response *response;
1153   const union MHD_ConnectionInfo *conn_info;
1154   const struct sockaddr *client_addr;
1155   const struct sockaddr_in *addrin;
1156   const struct sockaddr_in6 *addrin6;
1157   char address[INET6_ADDRSTRLEN + 14];
1158   struct GNUNET_PeerIdentity pi_in;
1159   size_t id_num = 0;
1160   struct IPv4HttpAddress ipv4addr;
1161   struct IPv6HttpAddress ipv6addr;
1162   struct HTTP_PeerContext *pc = NULL;
1163   struct Session *ps = NULL;
1164   struct Session *ps_tmp = NULL;
1165   int res = GNUNET_NO;
1166   void *addr = NULL;
1167   size_t addr_len = 0;
1168
1169   GNUNET_assert (cls != NULL);
1170
1171   if (NULL == *httpSessionCache)
1172   {
1173     /* check url for peer identity , if invalid send HTTP 404 */
1174     size_t len = strlen (&url[1]);
1175     char *peer = GNUNET_malloc (104 + 1);
1176
1177     if ((len > 104) && (url[104] == ';'))
1178     {
1179       char *id = GNUNET_malloc ((len - 104) + 1);
1180
1181       strcpy (id, &url[105]);
1182       memcpy (peer, &url[1], 103);
1183       peer[103] = '\0';
1184       id_num = strtoul (id, NULL, 10);
1185       GNUNET_free (id);
1186     }
1187     res = GNUNET_CRYPTO_hash_from_string (peer, &(pi_in.hashPubKey));
1188     GNUNET_free (peer);
1189     if (GNUNET_SYSERR == res)
1190     {
1191       response =
1192           MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),
1193                                          HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
1194       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
1195       MHD_destroy_response (response);
1196 #if DEBUG_CONNECTIONS
1197       if (res == MHD_YES)
1198         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1199                     "Peer has no valid ident, sent HTTP 1.1/404\n");
1200       else
1201         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1202                     "Peer has no valid ident, could not send error\n");
1203 #endif
1204       return res;
1205     }
1206   }
1207   else
1208   {
1209     ps = *httpSessionCache;
1210     pc = ps->peercontext;
1211   }
1212
1213   if (NULL == *httpSessionCache)
1214   {
1215     /* get peer context */
1216     pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &pi_in.hashPubKey);
1217     /* Peer unknown */
1218     if (pc == NULL)
1219     {
1220       pc = GNUNET_malloc (sizeof (struct HTTP_PeerContext));
1221       pc->plugin = plugin;
1222       pc->session_id_counter = 1;
1223       pc->last_session = NULL;
1224       memcpy (&pc->identity, &pi_in, sizeof (struct GNUNET_PeerIdentity));
1225       GNUNET_CONTAINER_multihashmap_put (plugin->peers,
1226                                          &pc->identity.hashPubKey, pc,
1227                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1228       GNUNET_STATISTICS_update (plugin->env->stats,
1229                                 gettext_noop ("# HTTP peers active"), 1,
1230                                 GNUNET_NO);
1231     }
1232
1233     conn_info =
1234         MHD_get_connection_info (mhd_connection,
1235                                  MHD_CONNECTION_INFO_CLIENT_ADDRESS);
1236     /* Incoming IPv4 connection */
1237     /* cast required for legacy MHD API < 0.9.6 */
1238     client_addr = (const struct sockaddr *) conn_info->client_addr;
1239     if (AF_INET == client_addr->sa_family)
1240     {
1241       addrin = (const struct sockaddr_in *) client_addr;
1242       inet_ntop (addrin->sin_family, &(addrin->sin_addr), address,
1243                  INET_ADDRSTRLEN);
1244       memcpy (&ipv4addr.ipv4_addr, &(addrin->sin_addr),
1245               sizeof (struct in_addr));
1246       ipv4addr.port = addrin->sin_port;
1247       addr = &ipv4addr;
1248       addr_len = sizeof (struct IPv4HttpAddress);
1249     }
1250     /* Incoming IPv6 connection */
1251     if (AF_INET6 == client_addr->sa_family)
1252     {
1253       addrin6 = (const struct sockaddr_in6 *) client_addr;
1254       inet_ntop (addrin6->sin6_family, &(addrin6->sin6_addr), address,
1255                  INET6_ADDRSTRLEN);
1256       memcpy (&ipv6addr.ipv6_addr, &(addrin6->sin6_addr),
1257               sizeof (struct in6_addr));
1258       ipv6addr.port = addrin6->sin6_port;
1259       addr = &ipv6addr;
1260       addr_len = sizeof (struct IPv6HttpAddress);
1261     }
1262
1263     GNUNET_assert (addr != NULL);
1264     GNUNET_assert (addr_len != 0);
1265
1266     ps = NULL;
1267     /* only inbound sessions here */
1268
1269     ps_tmp = pc->head;
1270     while (ps_tmp != NULL)
1271     {
1272       if ((ps_tmp->direction == INBOUND) && (ps_tmp->session_id == id_num) &&
1273           (id_num != 0))
1274       {
1275         if ((ps_tmp->recv_force_disconnect != GNUNET_YES) &&
1276             (ps_tmp->send_force_disconnect != GNUNET_YES))
1277           ps = ps_tmp;
1278         break;
1279       }
1280       ps_tmp = ps_tmp->next;
1281     }
1282
1283     if (ps == NULL)
1284     {
1285       ps = GNUNET_malloc (sizeof (struct Session));
1286       ps->addr = GNUNET_malloc (addr_len);
1287       memcpy (ps->addr, addr, addr_len);
1288       ps->addrlen = addr_len;
1289       ps->direction = INBOUND;
1290       ps->pending_msgs_head = NULL;
1291       ps->pending_msgs_tail = NULL;
1292       ps->send_connected = GNUNET_NO;
1293       ps->send_active = GNUNET_NO;
1294       ps->recv_connected = GNUNET_NO;
1295       ps->recv_active = GNUNET_NO;
1296       ps->peercontext = pc;
1297       ps->session_id = id_num;
1298       ps->queue_length_cur = 0;
1299       ps->queue_length_max = GNUNET_SERVER_MAX_MESSAGE_SIZE;
1300       ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
1301       GNUNET_CONTAINER_DLL_insert (pc->head, pc->tail, ps);
1302       GNUNET_STATISTICS_update (plugin->env->stats,
1303                                 gettext_noop
1304                                 ("# HTTP inbound sessions for peers active"), 1,
1305                                 GNUNET_NO);
1306     }
1307
1308     *httpSessionCache = ps;
1309     if (ps->msgtok == NULL)
1310       ps->msgtok = GNUNET_SERVER_mst_create (&mhd_write_mst_cb, ps);
1311 #if DEBUG_HTTP
1312     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1313                 "Connection %X: HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
1314                 ps, method, GNUNET_i2s (&pc->identity),
1315                 http_plugin_address_to_string (NULL, ps->addr, ps->addrlen));
1316 #endif
1317   }
1318
1319   /* Is it a PUT or a GET request */
1320   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
1321   {
1322     if (ps->recv_force_disconnect == GNUNET_YES)
1323     {
1324 #if DEBUG_CONNECTIONS
1325       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1326                   "Connection %X: inbound connection was forced to disconnect\n",
1327                   ps);
1328 #endif
1329       ps->recv_active = GNUNET_NO;
1330       return MHD_NO;
1331     }
1332     if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_NO))
1333     {
1334       ps->recv_endpoint = mhd_connection;
1335       ps->recv_connected = GNUNET_YES;
1336       ps->recv_active = GNUNET_YES;
1337       ps->recv_force_disconnect = GNUNET_NO;
1338 #if DEBUG_CONNECTIONS
1339       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1340                   "Connection %X: inbound PUT connection connected\n", ps);
1341 #endif
1342       return MHD_YES;
1343     }
1344
1345     /* Transmission of all data complete */
1346     if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_YES))
1347     {
1348       response =
1349           MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),
1350                                          HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
1351       res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1352 #if DEBUG_CONNECTIONS
1353       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1354                   "Connection %X: Sent HTTP/1.1: 200 OK as PUT Response\n", ps);
1355 #endif
1356       MHD_destroy_response (response);
1357       ps->recv_active = GNUNET_NO;
1358       return MHD_YES;
1359     }
1360
1361     /* Recieving data */
1362     if ((*upload_data_size > 0) && (ps->recv_active == GNUNET_YES))
1363     {
1364       if (pc->delay.rel_value == 0)
1365       {
1366 #if DEBUG_HTTP
1367         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1368                     "Connection %X: PUT with %u bytes forwarded to MST\n", ps,
1369                     *upload_data_size);
1370 #endif
1371         res =
1372             GNUNET_SERVER_mst_receive (ps->msgtok, ps, upload_data,
1373                                        *upload_data_size, GNUNET_NO, GNUNET_NO);
1374         (*upload_data_size) = 0;
1375       }
1376       else
1377       {
1378 #if DEBUG_HTTP
1379         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1380                     "Connection %X: no inbound bandwidth available! Next read was delayed for  %llu ms\n",
1381                     ps, ps->peercontext->delay.rel_value);
1382 #endif
1383       }
1384       return MHD_YES;
1385     }
1386     else
1387       return MHD_NO;
1388   }
1389   if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
1390   {
1391     if (ps->send_force_disconnect == GNUNET_YES)
1392     {
1393 #if DEBUG_CONNECTIONS
1394       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1395                   "Connection %X: outbound connection was  forced to disconnect\n",
1396                   ps);
1397 #endif
1398       ps->send_active = GNUNET_NO;
1399       return MHD_NO;
1400     }
1401     ps->send_connected = GNUNET_YES;
1402     ps->send_active = GNUNET_YES;
1403     ps->send_endpoint = mhd_connection;
1404     ps->send_force_disconnect = GNUNET_NO;
1405 #if DEBUG_CONNECTIONS
1406     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1407                 "Connection %X: inbound GET connection connected\n", ps);
1408 #endif
1409     response =
1410         MHD_create_response_from_callback (-1, 32 * 1024, &mhd_send_callback,
1411                                            ps, NULL);
1412     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1413     MHD_destroy_response (response);
1414     return MHD_YES;
1415   }
1416   return MHD_NO;
1417 }
1418
1419
1420 /**
1421  * Function that queries MHD's select sets and
1422  * starts the task waiting for them.
1423  * @param plugin plugin
1424  * @param daemon_handle the MHD daemon handle
1425  * @return gnunet task identifier
1426  */
1427 static GNUNET_SCHEDULER_TaskIdentifier
1428 http_server_daemon_prepare (struct Plugin *plugin,
1429                             struct MHD_Daemon *daemon_handle)
1430 {
1431   GNUNET_SCHEDULER_TaskIdentifier ret;
1432   fd_set rs;
1433   fd_set ws;
1434   fd_set es;
1435   struct GNUNET_NETWORK_FDSet *wrs;
1436   struct GNUNET_NETWORK_FDSet *wws;
1437   struct GNUNET_NETWORK_FDSet *wes;
1438   int max;
1439   unsigned long long timeout;
1440   int haveto;
1441   struct GNUNET_TIME_Relative tv;
1442
1443   ret = GNUNET_SCHEDULER_NO_TASK;
1444   FD_ZERO (&rs);
1445   FD_ZERO (&ws);
1446   FD_ZERO (&es);
1447   wrs = GNUNET_NETWORK_fdset_create ();
1448   wes = GNUNET_NETWORK_fdset_create ();
1449   wws = GNUNET_NETWORK_fdset_create ();
1450   max = -1;
1451   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
1452   haveto = MHD_get_timeout (daemon_handle, &timeout);
1453   if (haveto == MHD_YES)
1454     tv.rel_value = (uint64_t) timeout;
1455   else
1456     tv = GNUNET_TIME_UNIT_SECONDS;
1457   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1458   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1459   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1460   if (daemon_handle == plugin->http_server_daemon_v4)
1461   {
1462     if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1463     {
1464       GNUNET_SCHEDULER_cancel (plugin->http_server_task_v4);
1465       plugin->http_server_daemon_v4 = GNUNET_SCHEDULER_NO_TASK;
1466     }
1467
1468     ret =
1469         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1470                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
1471                                      &http_server_daemon_v4_run, plugin);
1472   }
1473   if (daemon_handle == plugin->http_server_daemon_v6)
1474   {
1475     if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1476     {
1477       GNUNET_SCHEDULER_cancel (plugin->http_server_task_v6);
1478       plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1479     }
1480
1481     ret =
1482         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1483                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
1484                                      &http_server_daemon_v6_run, plugin);
1485   }
1486   GNUNET_NETWORK_fdset_destroy (wrs);
1487   GNUNET_NETWORK_fdset_destroy (wws);
1488   GNUNET_NETWORK_fdset_destroy (wes);
1489   return ret;
1490 }
1491
1492
1493 /**
1494  * Call MHD IPv4 to process pending requests and then go back
1495  * and schedule the next run.
1496  * @param cls plugin as closure
1497  * @param tc task context
1498  */
1499 static void
1500 http_server_daemon_v4_run (void *cls,
1501                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1502 {
1503   struct Plugin *plugin = cls;
1504
1505 #if DEBUG_SCHEDULING
1506   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
1507     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1508                 "http_server_daemon_v4_run: GNUNET_SCHEDULER_REASON_READ_READY\n");
1509   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
1510     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1511                 "http_server_daemon_v4_run: GNUNET_SCHEDULER_REASON_WRITE_READY\n");
1512   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
1513     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1514                 "http_server_daemon_v4_run: GNUNET_SCHEDULER_REASON_TIMEOUT\n");
1515   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_STARTUP))
1516     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1517                 "http_server_daemon_v4_run: GGNUNET_SCHEDULER_REASON_STARTUP\n");
1518   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1519     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1520                 "http_server_daemon_v4_run: GGNUNET_SCHEDULER_REASON_SHUTDOWN\n");
1521 #endif
1522
1523   GNUNET_assert (cls != NULL);
1524   plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1525
1526   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1527     return;
1528
1529   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4));
1530   plugin->http_server_task_v4 =
1531       http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
1532 }
1533
1534
1535 /**
1536  * Call MHD IPv6 to process pending requests and then go back
1537  * and schedule the next run.
1538  * @param cls plugin as closure
1539  * @param tc task context
1540  */
1541 static void
1542 http_server_daemon_v6_run (void *cls,
1543                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1544 {
1545   struct Plugin *plugin = cls;
1546
1547 #if DEBUG_SCHEDULING
1548   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
1549     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1550                 "http_server_daemon_v6_run: GNUNET_SCHEDULER_REASON_READ_READY\n");
1551   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
1552     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1553                 "http_server_daemon_v6_run: GNUNET_SCHEDULER_REASON_WRITE_READY\n");
1554   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
1555     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1556                 "http_server_daemon_v6_run: GNUNET_SCHEDULER_REASON_TIMEOUT\n");
1557   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_STARTUP))
1558     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1559                 "http_server_daemon_v6_run: GGNUNET_SCHEDULER_REASON_STARTUP\n");
1560   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1561     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1562                 "http_server_daemon_v6_run: GGNUNET_SCHEDULER_REASON_SHUTDOWN\n");
1563 #endif
1564
1565   GNUNET_assert (cls != NULL);
1566   plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1567
1568   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1569     return;
1570
1571   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6));
1572   plugin->http_server_task_v6 =
1573       http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
1574 }
1575
1576
1577 static size_t
1578 curl_get_header_cb (void *ptr, size_t size, size_t nmemb, void *stream)
1579 {
1580   struct Session *ps = stream;
1581
1582   long http_result = 0;
1583   int res;
1584
1585   /* Getting last http result code */
1586   GNUNET_assert (NULL != ps);
1587   if (ps->recv_connected == GNUNET_NO)
1588   {
1589     res =
1590         curl_easy_getinfo (ps->recv_endpoint, CURLINFO_RESPONSE_CODE,
1591                            &http_result);
1592     if (CURLE_OK == res)
1593     {
1594       if (http_result == 200)
1595       {
1596         ps->recv_connected = GNUNET_YES;
1597         ps->recv_active = GNUNET_YES;
1598 #if DEBUG_CONNECTIONS
1599         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1600                     "Connection %X: connected to recieve data\n", ps);
1601 #endif
1602         // Calling send_check_connections again since receive is established
1603         send_check_connections (ps->peercontext->plugin, ps);
1604       }
1605     }
1606   }
1607
1608 #if DEBUG_CURL
1609   char *tmp;
1610   size_t len = size * nmemb;
1611
1612   tmp = NULL;
1613   if ((size * nmemb) < SIZE_MAX)
1614     tmp = GNUNET_malloc (len + 1);
1615
1616   if ((tmp != NULL) && (len > 0))
1617   {
1618     memcpy (tmp, ptr, len);
1619     if (len >= 2)
1620     {
1621       if (tmp[len - 2] == 13)
1622         tmp[len - 2] = '\0';
1623     }
1624     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection %X: Header: %s\n", ps,
1625                 tmp);
1626   }
1627   GNUNET_free_non_null (tmp);
1628 #endif
1629
1630   return size * nmemb;
1631 }
1632
1633
1634 /**
1635  * Callback called by libcurl when new headers arrive
1636  * Used to get HTTP result for curl operations
1637  * @param ptr stream to read from
1638  * @param size size of one char element
1639  * @param nmemb number of char elements
1640  * @param stream closure set by user
1641  * @return bytes read by function
1642  */
1643 static size_t
1644 curl_put_header_cb (void *ptr, size_t size, size_t nmemb, void *stream)
1645 {
1646   struct Session *ps = stream;
1647
1648   char *tmp;
1649   size_t len = size * nmemb;
1650   long http_result = 0;
1651   int res;
1652
1653   /* Getting last http result code */
1654   GNUNET_assert (NULL != ps);
1655   res =
1656       curl_easy_getinfo (ps->send_endpoint, CURLINFO_RESPONSE_CODE,
1657                          &http_result);
1658   if (CURLE_OK == res)
1659   {
1660     if ((http_result == 100) && (ps->send_connected == GNUNET_NO))
1661     {
1662       ps->send_connected = GNUNET_YES;
1663       ps->send_active = GNUNET_YES;
1664 #if DEBUG_CONNECTIONS
1665       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1666                   "Connection %X: connected to send data\n", ps);
1667 #endif
1668     }
1669     if ((http_result == 200) && (ps->send_connected == GNUNET_YES))
1670     {
1671       ps->send_connected = GNUNET_NO;
1672       ps->send_active = GNUNET_NO;
1673 #if DEBUG_CONNECTIONS
1674       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1675                   "Connection %X: sending disconnected\n", ps);
1676 #endif
1677     }
1678   }
1679
1680   tmp = NULL;
1681   if ((size * nmemb) < SIZE_MAX)
1682     tmp = GNUNET_malloc (len + 1);
1683
1684   if ((tmp != NULL) && (len > 0))
1685   {
1686     memcpy (tmp, ptr, len);
1687     if (len >= 2)
1688     {
1689       if (tmp[len - 2] == 13)
1690         tmp[len - 2] = '\0';
1691     }
1692   }
1693   GNUNET_free_non_null (tmp);
1694   return size * nmemb;
1695 }
1696
1697 /**
1698  * Callback method used with libcurl
1699  * Method is called when libcurl needs to read data during sending
1700  * @param stream pointer where to write data
1701  * @param size size of an individual element
1702  * @param nmemb count of elements that can be written to the buffer
1703  * @param ptr source pointer, passed to the libcurl handle
1704  * @return bytes written to stream
1705  */
1706 static size_t
1707 curl_send_cb (void *stream, size_t size, size_t nmemb, void *ptr)
1708 {
1709   struct Session *ps = ptr;
1710   struct HTTP_Message *msg = ps->pending_msgs_tail;
1711   size_t bytes_sent;
1712   size_t len;
1713
1714   if (ps->send_active == GNUNET_NO)
1715     return CURL_READFUNC_PAUSE;
1716   if ((ps->pending_msgs_tail == NULL) && (ps->send_active == GNUNET_YES))
1717   {
1718 #if DEBUG_CONNECTIONS
1719     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1720                 "Connection %X: No Message to send, pausing connection\n", ps);
1721 #endif
1722     ps->send_active = GNUNET_NO;
1723     return CURL_READFUNC_PAUSE;
1724   }
1725
1726   GNUNET_assert (msg != NULL);
1727
1728   /* data to send */
1729   if (msg->pos < msg->size)
1730   {
1731     /* data fit in buffer */
1732     if ((msg->size - msg->pos) <= (size * nmemb))
1733     {
1734       len = (msg->size - msg->pos);
1735       memcpy (stream, &msg->buf[msg->pos], len);
1736       msg->pos += len;
1737       bytes_sent = len;
1738     }
1739     else
1740     {
1741       len = size * nmemb;
1742       memcpy (stream, &msg->buf[msg->pos], len);
1743       msg->pos += len;
1744       bytes_sent = len;
1745     }
1746   }
1747   /* no data to send */
1748   else
1749   {
1750     bytes_sent = 0;
1751   }
1752
1753   if (msg->pos == msg->size)
1754   {
1755 #if DEBUG_CONNECTIONS
1756     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1757                 "Connection %X: Message with %u bytes sent, removing message from queue\n",
1758                 ps, msg->pos);
1759 #endif
1760     /* Calling transmit continuation  */
1761     if (NULL != ps->pending_msgs_tail->transmit_cont)
1762       msg->transmit_cont (ps->pending_msgs_tail->transmit_cont_cls,
1763                           &(ps->peercontext)->identity, GNUNET_OK);
1764     ps->queue_length_cur -= msg->size;
1765     remove_http_message (ps, msg);
1766   }
1767   return bytes_sent;
1768 }
1769
1770
1771 static void
1772 curl_receive_mst_cb (void *cls, void *client,
1773                      const struct GNUNET_MessageHeader *message)
1774 {
1775   struct Session *ps = cls;
1776   struct GNUNET_TIME_Relative delay;
1777
1778   GNUNET_assert (ps != NULL);
1779
1780   struct HTTP_PeerContext *pc = ps->peercontext;
1781
1782   GNUNET_assert (pc != NULL);
1783 #if DEBUG_HTTP
1784   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1785               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
1786               ps, ntohs (message->type), ntohs (message->size),
1787               GNUNET_i2s (&(pc->identity)), http_plugin_address_to_string (NULL,
1788                                                                            ps->
1789                                                                            addr,
1790                                                                            ps->
1791                                                                            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), "%s:%u", address, port);
2909
2910   GNUNET_free (address);
2911   GNUNET_assert (res != 0);
2912   return rbuf;
2913 }
2914
2915 /**
2916  * Function called by the NAT subsystem suggesting another peer wants
2917  * to connect to us via connection reversal.  Try to connect back to the
2918  * given IP.
2919  *
2920  * @param cls closure
2921  * @param addr address to try
2922  * @param addrlen number of bytes in addr
2923  */
2924 static void
2925 try_connection_reversal (void *cls, const struct sockaddr *addr,
2926                          socklen_t addrlen)
2927 {
2928
2929 }
2930
2931 static void
2932 tcp_nat_cb_add_addr (void *cls, int add_remove, const struct sockaddr *addr,
2933                      socklen_t addrlen)
2934 {
2935   struct Plugin *plugin = cls;
2936   struct IPv4HttpAddress *t4 = NULL;
2937   struct IPv4HttpAddressWrapper *w_t4 = NULL;
2938   struct IPv6HttpAddress *t6 = NULL;
2939   struct IPv6HttpAddressWrapper *w_t6 = NULL;
2940   int af;
2941
2942   af = addr->sa_family;
2943   switch (af)
2944   {
2945   case AF_INET:
2946     w_t4 = plugin->ipv4_addr_head;
2947     while (w_t4 != NULL)
2948     {
2949       int res = memcmp (&w_t4->addr->ipv4_addr,
2950                         &((struct sockaddr_in *) addr)->sin_addr,
2951                         sizeof (struct in_addr));
2952
2953       if (0 == res)
2954         break;
2955       w_t4 = w_t4->next;
2956     }
2957     if (w_t4 == NULL)
2958     {
2959       w_t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddressWrapper));
2960       t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddress));
2961       memcpy (&t4->ipv4_addr, &((struct sockaddr_in *) addr)->sin_addr,
2962               sizeof (struct in_addr));
2963       t4->port = htons (plugin->port_inbound);
2964
2965       w_t4->addr = t4;
2966
2967       GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head,
2968                                    plugin->ipv4_addr_tail, w_t4);
2969     }
2970     plugin->env->notify_address (plugin->env->cls, add_remove, w_t4->addr,
2971                                  sizeof (struct IPv4HttpAddress));
2972
2973     break;
2974   case AF_INET6:
2975     w_t6 = plugin->ipv6_addr_head;
2976     while (w_t6)
2977     {
2978       int res = memcmp (&w_t6->addr->ipv6_addr,
2979                         &((struct sockaddr_in6 *) addr)->sin6_addr,
2980                         sizeof (struct in6_addr));
2981
2982       if (0 == res)
2983         break;
2984       w_t6 = w_t6->next;
2985     }
2986     if (w_t6 == NULL)
2987     {
2988       w_t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddressWrapper));
2989       t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddress));
2990
2991       memcpy (&t6->ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
2992               sizeof (struct in6_addr));
2993       t6->port = htons (plugin->port_inbound);
2994
2995       w_t6->addr = t6;
2996
2997       GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head,
2998                                    plugin->ipv6_addr_tail, w_t6);
2999     }
3000     plugin->env->notify_address (plugin->env->cls, add_remove, w_t6->addr,
3001                                  sizeof (struct IPv6HttpAddress));
3002     break;
3003   default:
3004     return;
3005   }
3006
3007 }
3008
3009 static void
3010 tcp_nat_cb_remove_addr (void *cls, int add_remove, const struct sockaddr *addr,
3011                         socklen_t addrlen)
3012 {
3013   struct Plugin *plugin = cls;
3014   struct IPv4HttpAddressWrapper *w_t4 = NULL;
3015   struct IPv6HttpAddressWrapper *w_t6 = NULL;
3016   int af;
3017
3018   af = addr->sa_family;
3019   switch (af)
3020   {
3021   case AF_INET:
3022     w_t4 = plugin->ipv4_addr_head;
3023     while (w_t4 != NULL)
3024     {
3025       int res = memcmp (&w_t4->addr->ipv4_addr,
3026                         &((struct sockaddr_in *) addr)->sin_addr,
3027                         sizeof (struct in_addr));
3028
3029       if (0 == res)
3030         break;
3031       w_t4 = w_t4->next;
3032     }
3033     if (w_t4 == NULL)
3034       return;
3035     plugin->env->notify_address (plugin->env->cls, add_remove, w_t4->addr,
3036                                  sizeof (struct IPv4HttpAddress));
3037
3038     GNUNET_CONTAINER_DLL_remove (plugin->ipv4_addr_head, plugin->ipv4_addr_tail,
3039                                  w_t4);
3040     GNUNET_free (w_t4->addr);
3041     GNUNET_free (w_t4);
3042     break;
3043   case AF_INET6:
3044     w_t6 = plugin->ipv6_addr_head;
3045     while (w_t6 != NULL)
3046     {
3047       int res = memcmp (&w_t6->addr->ipv6_addr,
3048                         &((struct sockaddr_in6 *) addr)->sin6_addr,
3049                         sizeof (struct in6_addr));
3050
3051       if (0 == res)
3052         break;
3053       w_t6 = w_t6->next;
3054     }
3055     if (w_t6 == NULL)
3056       return;
3057     plugin->env->notify_address (plugin->env->cls, add_remove, w_t6->addr,
3058                                  sizeof (struct IPv6HttpAddress));
3059
3060     GNUNET_CONTAINER_DLL_remove (plugin->ipv6_addr_head, plugin->ipv6_addr_tail,
3061                                  w_t6);
3062     GNUNET_free (w_t6->addr);
3063     GNUNET_free (w_t6);
3064     break;
3065   default:
3066     return;
3067   }
3068
3069 }
3070
3071 /**
3072  * Our external IP address/port mapping has changed.
3073  *
3074  * @param cls closure, the 'struct LocalAddrList'
3075  * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
3076  *     the previous (now invalid) one
3077  * @param addr either the previous or the new public IP address
3078  * @param addrlen actual lenght of the address
3079  */
3080 static void
3081 tcp_nat_port_map_callback (void *cls, int add_remove,
3082                            const struct sockaddr *addr, socklen_t addrlen)
3083 {
3084   GNUNET_assert (cls != NULL);
3085 #if DEBUG_HTTP
3086   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NPMC called to %s address `%s'\n",
3087                    (add_remove == GNUNET_YES) ? "remove" : "add",
3088                    GNUNET_a2s (addr, addrlen));
3089 #endif
3090   /* convert 'addr' to our internal format */
3091   switch (add_remove)
3092   {
3093   case GNUNET_YES:
3094     tcp_nat_cb_add_addr (cls, add_remove, addr, addrlen);
3095     break;
3096   case GNUNET_NO:
3097     tcp_nat_cb_remove_addr (cls, add_remove, addr, addrlen);
3098     break;
3099   }
3100 }
3101
3102 /**
3103  * Exit point from the plugin.
3104  */
3105 void *
3106 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
3107 {
3108   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
3109   struct Plugin *plugin = api->cls;
3110   CURLMcode mret;
3111   struct IPv4HttpAddressWrapper *w_t4;
3112   struct IPv6HttpAddressWrapper *w_t6;
3113
3114   GNUNET_assert (cls != NULL);
3115
3116   if (plugin->nat != NULL)
3117     GNUNET_NAT_unregister (plugin->nat);
3118
3119   if (plugin->http_server_daemon_v4 != NULL)
3120   {
3121     MHD_stop_daemon (plugin->http_server_daemon_v4);
3122     plugin->http_server_daemon_v4 = NULL;
3123   }
3124   if (plugin->http_server_daemon_v6 != NULL)
3125   {
3126     MHD_stop_daemon (plugin->http_server_daemon_v6);
3127     plugin->http_server_daemon_v6 = NULL;
3128   }
3129   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
3130   {
3131     GNUNET_SCHEDULER_cancel (plugin->http_server_task_v4);
3132     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
3133   }
3134   if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
3135   {
3136     GNUNET_SCHEDULER_cancel (plugin->http_server_task_v6);
3137     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
3138   }
3139
3140   while (plugin->ipv4_addr_head != NULL)
3141   {
3142     w_t4 = plugin->ipv4_addr_head;
3143     GNUNET_CONTAINER_DLL_remove (plugin->ipv4_addr_head, plugin->ipv4_addr_tail,
3144                                  w_t4);
3145     GNUNET_free (w_t4->addr);
3146     GNUNET_free (w_t4);
3147   }
3148
3149   while (plugin->ipv6_addr_head != NULL)
3150   {
3151     w_t6 = plugin->ipv6_addr_head;
3152     GNUNET_CONTAINER_DLL_remove (plugin->ipv6_addr_head, plugin->ipv6_addr_tail,
3153                                  w_t6);
3154     GNUNET_free (w_t6->addr);
3155     GNUNET_free (w_t6);
3156   }
3157
3158   /* free all peer information */
3159   if (plugin->peers != NULL)
3160   {
3161     GNUNET_CONTAINER_multihashmap_iterate (plugin->peers,
3162                                            &remove_peer_context_Iterator,
3163                                            plugin);
3164     GNUNET_CONTAINER_multihashmap_destroy (plugin->peers);
3165   }
3166   if (plugin->multi_handle != NULL)
3167   {
3168     mret = curl_multi_cleanup (plugin->multi_handle);
3169     if (CURLM_OK != mret)
3170       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3171                   "curl multihandle clean up failed\n");
3172     plugin->multi_handle = NULL;
3173   }
3174   curl_global_cleanup ();
3175
3176   if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
3177   {
3178     GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
3179     plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
3180   }
3181
3182
3183   GNUNET_free_non_null (plugin->bind4_address);
3184   GNUNET_free_non_null (plugin->bind6_address);
3185   GNUNET_free_non_null (plugin->bind_hostname);
3186 #if BUILD_HTTPS
3187   GNUNET_free_non_null (plugin->crypto_init);
3188   GNUNET_free_non_null (plugin->cert);
3189   GNUNET_free_non_null (plugin->key);
3190 #endif
3191   GNUNET_free (plugin);
3192   GNUNET_free (api);
3193 #if DEBUG_HTTP
3194   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Unload %s plugin complete...\n",
3195               PROTOCOL_PREFIX);
3196 #endif
3197   return NULL;
3198 }
3199
3200 #if BUILD_HTTPS
3201 static char *
3202 load_certificate (const char *file)
3203 {
3204   struct GNUNET_DISK_FileHandle *gn_file;
3205   struct stat fstat;
3206   char *text = NULL;
3207
3208   if (0 != STAT (file, &fstat))
3209     return NULL;
3210   text = GNUNET_malloc (fstat.st_size + 1);
3211   gn_file =
3212       GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
3213                              GNUNET_DISK_PERM_USER_READ);
3214   if (gn_file == NULL)
3215   {
3216     GNUNET_free (text);
3217     return NULL;
3218   }
3219   if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fstat.st_size))
3220   {
3221     GNUNET_free (text);
3222     GNUNET_DISK_file_close (gn_file);
3223     return NULL;
3224   }
3225   text[fstat.st_size] = '\0';
3226   GNUNET_DISK_file_close (gn_file);
3227   return text;
3228 }
3229 #endif
3230
3231
3232 /**
3233  * Entry point for the plugin.
3234  */
3235 void *
3236 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
3237 {
3238   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
3239   struct Plugin *plugin;
3240   struct GNUNET_TRANSPORT_PluginFunctions *api;
3241   struct GNUNET_TIME_Relative gn_timeout;
3242   long long unsigned int port;
3243   unsigned long long tneigh;
3244   struct sockaddr **addrs;
3245   socklen_t *addrlens;
3246   int ret;
3247   char *component_name;
3248
3249 #if BUILD_HTTPS
3250   char *key_file = NULL;
3251   char *cert_file = NULL;
3252 #endif
3253
3254   GNUNET_assert (cls != NULL);
3255 #if DEBUG_HTTP
3256   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting %s plugin...\n",
3257               PROTOCOL_PREFIX);
3258 #endif
3259   GNUNET_asprintf (&component_name, "transport-%s", PROTOCOL_PREFIX);
3260
3261   plugin = GNUNET_malloc (sizeof (struct Plugin));
3262   plugin->stats = env->stats;
3263   plugin->env = env;
3264   plugin->peers = NULL;
3265   plugin->bind4_address = NULL;
3266   plugin->bind6_address = NULL;
3267   plugin->use_ipv6 = GNUNET_YES;
3268   plugin->use_ipv4 = GNUNET_YES;
3269   plugin->use_localaddresses = GNUNET_NO;
3270
3271   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
3272   api->cls = plugin;
3273   api->send = &http_plugin_send;
3274   api->disconnect = &http_plugin_disconnect;
3275   api->address_pretty_printer = &http_plugin_address_pretty_printer;
3276   api->check_address = &http_plugin_address_suggested;
3277   api->address_to_string = &http_plugin_address_to_string;
3278
3279   /* Hashing our identity to use it in URLs */
3280   GNUNET_CRYPTO_hash_to_enc (&(plugin->env->my_identity->hashPubKey),
3281                              &plugin->my_ascii_hash_ident);
3282
3283
3284   if (GNUNET_OK !=
3285       GNUNET_CONFIGURATION_get_value_number (env->cfg, component_name,
3286                                              "MAX_CONNECTIONS", &tneigh))
3287     tneigh = 128;
3288   plugin->max_connect_per_transport = tneigh;
3289
3290
3291   /* Use IPv6? */
3292   if (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "USE_IPv6"))
3293   {
3294     plugin->use_ipv6 =
3295         GNUNET_CONFIGURATION_get_value_yesno (env->cfg, component_name,
3296                                               "USE_IPv6");
3297   }
3298   /* Use IPv4? */
3299   if (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "USE_IPv4"))
3300   {
3301     plugin->use_ipv4 =
3302         GNUNET_CONFIGURATION_get_value_yesno (env->cfg, component_name,
3303                                               "USE_IPv4");
3304   }
3305   /* use local addresses? */
3306
3307   if (GNUNET_CONFIGURATION_have_value
3308       (env->cfg, component_name, "USE_LOCALADDR"))
3309   {
3310     plugin->use_localaddresses =
3311         GNUNET_CONFIGURATION_get_value_yesno (env->cfg, component_name,
3312                                               "USE_LOCALADDR");
3313   }
3314   /* Reading port number from config file */
3315   if ((GNUNET_OK !=
3316        GNUNET_CONFIGURATION_get_value_number (env->cfg, component_name, "PORT",
3317                                               &port)) || (port > 65535))
3318   {
3319     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, component_name,
3320                      _
3321                      ("Require valid port number for transport plugin `%s' in configuration!\n"),
3322                      PROTOCOL_PREFIX);
3323     GNUNET_free (component_name);
3324     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3325     return NULL;
3326   }
3327
3328   /* Reading ipv4 addresse to bind to from config file */
3329   if ((plugin->use_ipv4 == GNUNET_YES) &&
3330       (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "BINDTO4")))
3331   {
3332     GNUNET_break (GNUNET_OK ==
3333                   GNUNET_CONFIGURATION_get_value_string (env->cfg,
3334                                                          component_name,
3335                                                          "BINDTO4",
3336                                                          &plugin->
3337                                                          bind_hostname));
3338     plugin->bind4_address = GNUNET_malloc (sizeof (struct sockaddr_in));
3339     plugin->bind4_address->sin_family = AF_INET;
3340     plugin->bind4_address->sin_port = htons (port);
3341
3342     if (plugin->bind_hostname != NULL)
3343     {
3344       if (inet_pton
3345           (AF_INET, plugin->bind_hostname,
3346            &plugin->bind4_address->sin_addr) <= 0)
3347       {
3348         GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, component_name,
3349                          _
3350                          ("Misconfigured address to bind to in configuration!\n"));
3351         GNUNET_free (plugin->bind4_address);
3352         GNUNET_free (plugin->bind_hostname);
3353         plugin->bind_hostname = NULL;
3354         plugin->bind4_address = NULL;
3355       }
3356     }
3357   }
3358
3359   /* Reading ipv4 addresse to bind to from config file */
3360   if ((plugin->use_ipv6 == GNUNET_YES) &&
3361       (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "BINDTO6")))
3362   {
3363     if (GNUNET_OK ==
3364         GNUNET_CONFIGURATION_get_value_string (env->cfg, component_name,
3365                                                "BINDTO6",
3366                                                &plugin->bind_hostname))
3367     {
3368       plugin->bind6_address = GNUNET_malloc (sizeof (struct sockaddr_in6));
3369       plugin->bind6_address->sin6_family = AF_INET6;
3370       plugin->bind6_address->sin6_port = htons (port);
3371       if (plugin->bind_hostname != NULL)
3372       {
3373         if (inet_pton
3374             (AF_INET6, plugin->bind_hostname,
3375              &plugin->bind6_address->sin6_addr) <= 0)
3376         {
3377           GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, component_name,
3378                            _
3379                            ("Misconfigured address to bind to in configuration!\n"));
3380           GNUNET_free (plugin->bind6_address);
3381           GNUNET_free (plugin->bind_hostname);
3382           plugin->bind_hostname = NULL;
3383           plugin->bind6_address = NULL;
3384         }
3385       }
3386     }
3387   }
3388
3389 #if BUILD_HTTPS
3390   /* Reading HTTPS crypto related configuration */
3391   /* Get crypto init string from config */
3392   if ((GNUNET_OK !=
3393        GNUNET_CONFIGURATION_get_value_string (env->cfg, "transport-https",
3394                                               "CRYPTO_INIT",
3395                                               &plugin->crypto_init)) ||
3396       (GNUNET_OK !=
3397        GNUNET_CONFIGURATION_get_value_filename (env->cfg, "transport-https",
3398                                                 "KEY_FILE", &key_file)) ||
3399       (GNUNET_OK !=
3400        GNUNET_CONFIGURATION_get_value_filename (env->cfg, "transport-https",
3401                                                 "CERT_FILE", &cert_file)))
3402   {
3403     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "https",
3404                      _
3405                      ("Required configuration options missing in section `%s'\n"),
3406                      "transport-https");
3407     GNUNET_free (component_name);
3408     GNUNET_free_non_null (key_file);
3409     GNUNET_free_non_null (cert_file);
3410     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3411     return NULL;
3412   }
3413
3414   /* read key & certificates from file */
3415   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading TLS certificate `%s' `%s'\n",
3416               key_file, cert_file);
3417
3418   plugin->key = load_certificate (key_file);
3419   plugin->cert = load_certificate (cert_file);
3420
3421   if ((plugin->key == NULL) || (plugin->cert == NULL))
3422   {
3423     struct GNUNET_OS_Process *certcreation;
3424
3425     GNUNET_free_non_null (plugin->key);
3426     plugin->key = NULL;
3427     GNUNET_free_non_null (plugin->cert);
3428     plugin->cert = NULL;
3429 #if DEBUG_HTTP
3430     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3431                 "No usable TLS certificate found, creating certificate\n");
3432 #endif
3433     errno = 0;
3434     certcreation =
3435         GNUNET_OS_start_process (NULL, NULL,
3436                                  "gnunet-transport-certificate-creation",
3437                                  "gnunet-transport-certificate-creation",
3438                                  key_file, cert_file, NULL);
3439     if (certcreation == NULL)
3440     {
3441       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "https",
3442                        _
3443                        ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
3444       GNUNET_free (key_file);
3445       GNUNET_free (cert_file);
3446       GNUNET_free (component_name);
3447       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3448       return NULL;
3449     }
3450     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (certcreation));
3451     GNUNET_OS_process_close (certcreation);
3452     plugin->key = load_certificate (key_file);
3453     plugin->cert = load_certificate (cert_file);
3454   }
3455   if ((plugin->key == NULL) || (plugin->cert == NULL))
3456   {
3457     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "https",
3458                      _
3459                      ("No usable TLS certificate found and creating one failed!\n"),
3460                      "transport-https");
3461     GNUNET_free (key_file);
3462     GNUNET_free (cert_file);
3463     GNUNET_free (component_name);
3464     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3465     return NULL;
3466   }
3467   GNUNET_free (key_file);
3468   GNUNET_free (cert_file);
3469 #if DEBUG_HTTP
3470   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
3471 #endif
3472 #endif
3473
3474   GNUNET_assert ((port > 0) && (port <= 65535));
3475   plugin->port_inbound = port;
3476   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
3477   unsigned int timeout = (gn_timeout.rel_value) / 1000;
3478
3479   if ((plugin->http_server_daemon_v6 == NULL) &&
3480       (plugin->use_ipv6 == GNUNET_YES) && (port != 0))
3481   {
3482     struct sockaddr *tmp = (struct sockaddr *) plugin->bind6_address;
3483
3484     plugin->http_server_daemon_v6 = MHD_start_daemon (
3485 #if DEBUG_MHD
3486                                                        MHD_USE_DEBUG |
3487 #endif
3488 #if BUILD_HTTPS
3489                                                        MHD_USE_SSL |
3490 #endif
3491                                                        MHD_USE_IPv6, port,
3492                                                        &mhd_accept_cb, plugin,
3493                                                        &mhd_access_cb, plugin,
3494                                                        MHD_OPTION_SOCK_ADDR,
3495                                                        tmp,
3496                                                        MHD_OPTION_CONNECTION_LIMIT,
3497                                                        (unsigned int) plugin->
3498                                                        max_connect_per_transport,
3499 #if BUILD_HTTPS
3500                                                        MHD_OPTION_HTTPS_PRIORITIES,
3501                                                        plugin->crypto_init,
3502                                                        MHD_OPTION_HTTPS_MEM_KEY,
3503                                                        plugin->key,
3504                                                        MHD_OPTION_HTTPS_MEM_CERT,
3505                                                        plugin->cert,
3506 #endif
3507                                                        MHD_OPTION_CONNECTION_TIMEOUT,
3508                                                        (unsigned int) timeout,
3509                                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
3510                                                        (size_t) (2 *
3511                                                                  GNUNET_SERVER_MAX_MESSAGE_SIZE),
3512                                                        MHD_OPTION_NOTIFY_COMPLETED,
3513                                                        &mhd_termination_cb,
3514                                                        plugin,
3515                                                        MHD_OPTION_EXTERNAL_LOGGER,
3516                                                        mhd_logger,
3517                                                        plugin->mhd_log,
3518                                                        MHD_OPTION_END);
3519   }
3520   if ((plugin->http_server_daemon_v4 == NULL) &&
3521       (plugin->use_ipv4 == GNUNET_YES) && (port != 0))
3522   {
3523     plugin->http_server_daemon_v4 = MHD_start_daemon (
3524 #if DEBUG_MHD
3525                                                        MHD_USE_DEBUG |
3526 #endif
3527 #if BUILD_HTTPS
3528                                                        MHD_USE_SSL |
3529 #endif
3530                                                        MHD_NO_FLAG, port,
3531                                                        &mhd_accept_cb, plugin,
3532                                                        &mhd_access_cb, plugin,
3533                                                        MHD_OPTION_SOCK_ADDR,
3534                                                        (struct sockaddr_in *)
3535                                                        plugin->bind4_address,
3536                                                        MHD_OPTION_CONNECTION_LIMIT,
3537                                                        (unsigned int) plugin->
3538                                                        max_connect_per_transport,
3539 #if BUILD_HTTPS
3540                                                        MHD_OPTION_HTTPS_PRIORITIES,
3541                                                        plugin->crypto_init,
3542                                                        MHD_OPTION_HTTPS_MEM_KEY,
3543                                                        plugin->key,
3544                                                        MHD_OPTION_HTTPS_MEM_CERT,
3545                                                        plugin->cert,
3546 #endif
3547                                                        MHD_OPTION_CONNECTION_TIMEOUT,
3548                                                        (unsigned int) timeout,
3549                                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
3550                                                        (size_t) (2 *
3551                                                                  GNUNET_SERVER_MAX_MESSAGE_SIZE),
3552                                                        MHD_OPTION_NOTIFY_COMPLETED,
3553                                                        &mhd_termination_cb,
3554                                                        plugin,
3555                                                        MHD_OPTION_EXTERNAL_LOGGER,
3556                                                        mhd_logger,
3557                                                        plugin->mhd_log,
3558                                                        MHD_OPTION_END);
3559   }
3560   if (plugin->http_server_daemon_v4 != NULL)
3561     plugin->http_server_task_v4 =
3562         http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
3563   if (plugin->http_server_daemon_v6 != NULL)
3564     plugin->http_server_task_v6 =
3565         http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
3566
3567
3568   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
3569   {
3570 #if DEBUG_HTTP
3571     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3572                 "Starting MHD with IPv4 bound to %s with port %u\n",
3573                 (plugin->bind_hostname !=
3574                  NULL) ? plugin->bind_hostname : "every address", port);
3575 #endif
3576   }
3577   else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) &&
3578            (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK))
3579   {
3580 #if DEBUG_HTTP
3581     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3582                 "Starting MHD with IPv6 bound to %s with port %u\n",
3583                 (plugin->bind_hostname !=
3584                  NULL) ? plugin->bind_hostname : "every address", port);
3585 #endif
3586   }
3587   else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) &&
3588            (plugin->http_server_task_v4 == GNUNET_SCHEDULER_NO_TASK))
3589   {
3590 #if DEBUG_HTTP
3591     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3592                 "Starting MHD with IPv4 and IPv6 bound to %s with port %u\n",
3593                 (plugin->bind_hostname !=
3594                  NULL) ? plugin->bind_hostname : "every address", port);
3595 #endif
3596   }
3597   else
3598   {
3599     char *tmp = NULL;
3600
3601     if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_YES))
3602       GNUNET_asprintf (&tmp, "with IPv4 and IPv6 enabled");
3603     if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_YES))
3604       GNUNET_asprintf (&tmp, "with IPv4 enabled");
3605     if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_NO))
3606       GNUNET_asprintf (&tmp, "with IPv6 enabled");
3607     if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_NO))
3608       GNUNET_asprintf (&tmp, "with NO IP PROTOCOL enabled");
3609     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3610                 _
3611                 ("HTTP Server with %s could not be started on port %u! %s plugin failed!\n"),
3612                 tmp, port, PROTOCOL_PREFIX);
3613     GNUNET_free (tmp);
3614     GNUNET_free (component_name);
3615     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3616     return NULL;
3617   }
3618
3619   /* Initializing cURL */
3620   curl_global_init (CURL_GLOBAL_ALL);
3621   plugin->multi_handle = curl_multi_init ();
3622
3623   if (NULL == plugin->multi_handle)
3624   {
3625     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, component_name,
3626                      _
3627                      ("Could not initialize curl multi handle, failed to start %s plugin!\n"),
3628                      PROTOCOL_PREFIX);
3629     GNUNET_free (component_name);
3630     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3631     return NULL;
3632   }
3633
3634   ret =
3635       GNUNET_SERVICE_get_server_addresses (component_name, env->cfg, &addrs,
3636                                            &addrlens);
3637
3638   if (ret != GNUNET_SYSERR)
3639   {
3640     plugin->nat =
3641         GNUNET_NAT_register (env->cfg, GNUNET_YES, port, (unsigned int) ret,
3642                              (const struct sockaddr **) addrs, addrlens,
3643                              &tcp_nat_port_map_callback,
3644                              &try_connection_reversal, plugin);
3645     while (ret > 0)
3646     {
3647       ret--;
3648       GNUNET_assert (addrs[ret] != NULL);
3649       GNUNET_free (addrs[ret]);
3650     }
3651     GNUNET_free_non_null (addrs);
3652     GNUNET_free_non_null (addrlens);
3653   }
3654   else
3655   {
3656     plugin->nat =
3657         GNUNET_NAT_register (env->cfg, GNUNET_YES, 0, 0, NULL, NULL, NULL,
3658                              &try_connection_reversal, plugin);
3659   }
3660
3661   plugin->peers = GNUNET_CONTAINER_multihashmap_create (10);
3662
3663   GNUNET_free (component_name);
3664   //GNUNET_SCHEDULER_add_now(address_notification, plugin);
3665   return api;
3666 }
3667
3668 /* end of plugin_transport_http.c */