2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file transport/plugin_transport_http.c
23 * @brief http transport service plugin
24 * @author Matthias Wachs
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>
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"
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"
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
64 #define INBOUND GNUNET_NO
65 #define OUTBOUND GNUNET_YES
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
74 #define HTTP_PUT_RESPONSE "Thank you!"
77 * After how long do we expire an address that we
78 * learned from another peer if it is not reconfirmed
81 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
84 * Page returned if request invalid
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>"
89 * Timeout for a http connect
91 #define HTTP_CONNECT_TIMEOUT 30
94 * Network format for IPv4 addresses.
96 struct IPv4HttpAddress
99 * IPv4 address, in network byte order.
101 uint32_t ipv4_addr GNUNET_PACKED;
104 * Port number, in network byte order.
106 uint16_t port GNUNET_PACKED;
110 * Wrapper to manage IPv4 addresses
112 struct IPv4HttpAddressWrapper
117 struct IPv4HttpAddressWrapper *next;
120 * Linked list previous
122 struct IPv4HttpAddressWrapper *prev;
124 struct IPv4HttpAddress *addr;
128 * Network format for IPv6 addresses.
130 struct IPv6HttpAddress
135 struct in6_addr ipv6_addr GNUNET_PACKED;
138 * Port number, in network byte order.
140 uint16_t port GNUNET_PACKED;
145 * Wrapper for IPv4 addresses.
147 struct IPv6HttpAddressWrapper
152 struct IPv6HttpAddressWrapper *next;
155 * Linked list previous
157 struct IPv6HttpAddressWrapper *prev;
159 struct IPv6HttpAddress *addr;
163 * Message to send using http
168 * next pointer for double linked list
170 struct HTTP_Message *next;
173 * previous pointer for double linked list
175 struct HTTP_Message *prev;
178 * buffer containing data to send
183 * amount of data already sent
193 * Continuation function to call once the transmission buffer
194 * has again space available. NULL if there is no
195 * continuation to call.
197 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
200 * Closure for transmit_cont.
202 void *transmit_cont_cls;
206 struct HTTP_PeerContext
211 struct GNUNET_PeerIdentity identity;
214 * Pointer to the global plugin struct.
216 struct Plugin *plugin;
219 * Linked list of connections with this peer
222 struct Session *head;
225 * Linked list of connections with this peer
228 struct Session *tail;
231 * id for next session
233 size_t session_id_counter;
236 * Last session used to send data
238 struct Session *last_session;
241 * The task resetting inbound quota delay
243 GNUNET_SCHEDULER_TaskIdentifier reset_task;
246 * Delay from transport service inbound quota tracker when to receive data again
248 struct GNUNET_TIME_Relative delay;
257 struct SessionHeader header;
260 * next session in linked list
262 struct Session *next;
265 * previous session in linked list
267 struct Session *prev;
270 * address of this session
285 * Message queue for outbound messages
288 struct HTTP_Message *pending_msgs_head;
291 * Message queue for outbound messages
294 struct HTTP_Message *pending_msgs_tail;
297 * partner peer this connection belongs to
299 struct HTTP_PeerContext *peercontext;
302 * message stream tokenizer for incoming data
304 struct GNUNET_SERVER_MessageStreamTokenizer *msgtok;
308 * outbound: OUTBOUND (GNUNET_YES)
309 * inbound : INBOUND (GNUNET_NO)
311 unsigned int direction;
314 * is session connected to send data?
316 unsigned int send_connected;
319 * is send connection active?
321 unsigned int send_active;
324 * connection disconnect forced (e.g. from transport)
326 unsigned int send_force_disconnect;
329 * is session connected to receive data?
331 unsigned int recv_connected;
334 * is receive connection active?
336 unsigned int recv_active;
339 * connection disconnect forced (e.g. from transport)
341 unsigned int recv_force_disconnect;
345 * id for next session
346 * NOTE: 0 is not an ID, zero is not defined. A correct ID is always > 0
351 * entity managing sending data
352 * outbound session: CURL *
353 * inbound session: mhd_connection *
358 * entity managing recieving data
359 * outbound session: CURL *
360 * inbound session: mhd_connection *
367 size_t queue_length_cur;
372 size_t queue_length_max;
377 * Encapsulation of all of the state of the plugin.
384 struct GNUNET_TRANSPORT_PluginEnvironment *env;
387 * Handle for reporting statistics.
389 struct GNUNET_STATISTICS_Handle *stats;
394 uint16_t port_inbound;
396 struct GNUNET_CONTAINER_MultiHashMap *peers;
399 * Daemon for listening for new IPv4 connections.
401 struct MHD_Daemon *http_server_daemon_v4;
404 * Daemon for listening for new IPv6connections.
406 struct MHD_Daemon *http_server_daemon_v6;
409 * Our primary task for http daemon handling IPv4 connections
411 GNUNET_SCHEDULER_TaskIdentifier http_server_task_v4;
414 * Our primary task for http daemon handling IPv6 connections
416 GNUNET_SCHEDULER_TaskIdentifier http_server_task_v6;
419 * The task sending data
421 GNUNET_SCHEDULER_TaskIdentifier http_curl_task;
429 * Our handle to the NAT module.
431 struct GNUNET_NAT_Handle *nat;
436 struct IPv4HttpAddressWrapper *ipv4_addr_head;
441 struct IPv4HttpAddressWrapper *ipv4_addr_tail;
446 struct IPv6HttpAddressWrapper *ipv6_addr_head;
451 struct IPv6HttpAddressWrapper *ipv6_addr_tail;
454 * Our ASCII encoded, hashed peer identity
455 * This string is used to distinguish between connections and is added to the urls
457 struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
460 * IPv4 Address the plugin binds to
462 struct sockaddr_in *bind4_address;
465 * IPv6 Address the plugins binds to
467 struct sockaddr_in6 *bind6_address;
470 * Hostname to bind to
485 * use local addresses?
487 int use_localaddresses;
490 * maximum number of connections
492 int max_connect_per_transport;
495 * Current number of connections;
497 int current_connections;
500 * Closure passed by MHD to the mhd_logger function
504 /* only needed for HTTPS plugin */
506 /* The certificate MHD uses as an \0 terminated string */
509 /* The private key MHD uses as an \0 terminated string */
512 /* crypto init string */
518 * Context for address to string conversion.
520 struct PrettyPrinterContext
523 * Function to call with the result.
525 GNUNET_TRANSPORT_AddressStringCallback asc;
533 * Port to add after the IP address.
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.
546 * @param addr binary address
547 * @param addrlen length of the address
548 * @return string representing the same address
550 static const char *http_plugin_address_to_string (void *cls,
556 * Call MHD to process pending ipv4 requests and then go back
557 * and schedule the next run.
559 static void http_server_daemon_v4_run (void *cls,
560 const struct GNUNET_SCHEDULER_TaskContext
563 * Call MHD to process pending ipv6 requests and then go back
564 * and schedule the next run.
566 static void http_server_daemon_v6_run (void *cls,
567 const struct GNUNET_SCHEDULER_TaskContext
571 * Function setting up curl handle and selecting message to send
573 * @param plugin plugin
575 * @return GNUNET_SYSERR on failure, GNUNET_NO if connecting, GNUNET_YES if ok
577 static int send_check_connections (struct Plugin *plugin, struct Session *ps);
580 * Function setting up file descriptors and scheduling task to run
582 * @param plugin plugin as closure
583 * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
585 static int curl_schedule (struct Plugin *plugin);
588 * Task scheduled to reset the inbound quota delay for a specific peer
589 * @param cls plugin as closure
590 * @param tc task context
593 reset_inbound_quota_delay (void *cls,
594 const struct GNUNET_SCHEDULER_TaskContext *tc)
596 struct HTTP_PeerContext *pc = cls;
598 GNUNET_assert (cls != NULL);
599 pc->reset_task = GNUNET_SCHEDULER_NO_TASK;
600 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
602 pc->delay = GNUNET_TIME_relative_get_zero ();
607 * Creates a valid url from passed address and id
608 * @param plugin plugin
609 * @param addr address to create url from
610 * @param addrlen address lenth
611 * @param id session id
612 * @return the created url
615 create_url (struct Plugin *plugin, const void *addr, size_t addrlen, size_t id)
618 char *addr_str = (char *) http_plugin_address_to_string (NULL, addr, addrlen);
620 GNUNET_assert ((addr != NULL) && (addrlen != 0));
621 GNUNET_asprintf (&url,
622 "%s://%s/%s;%u", PROTOCOL_PREFIX, addr_str,
623 (char *) (&plugin->my_ascii_hash_ident), id);
629 * Removes a message from the linked list of messages
632 * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
635 remove_http_message (struct Session *ps, struct HTTP_Message *msg)
637 GNUNET_CONTAINER_DLL_remove (ps->pending_msgs_head,
638 ps->pending_msgs_tail, msg);
644 * Iterator to remove peer context
645 * @param cls the plugin
646 * @param key the peers public key hashcode
647 * @param value the peer context
648 * @return GNUNET_YES on success
651 remove_peer_context_Iterator (void *cls,
652 const GNUNET_HashCode * key, void *value)
654 struct Plugin *plugin = cls;
655 struct HTTP_PeerContext *pc = value;
656 struct Session *ps = pc->head;
657 struct Session *tmp = NULL;
658 struct HTTP_Message *msg = NULL;
659 struct HTTP_Message *msg_tmp = NULL;
662 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
663 "Freeing context for peer `%s'\n", GNUNET_i2s (&pc->identity));
665 GNUNET_assert (GNUNET_YES ==
666 GNUNET_CONTAINER_multihashmap_remove (plugin->peers,
667 &pc->identity.hashPubKey,
671 plugin->env->session_end (plugin, &pc->identity, ps);
674 GNUNET_free_non_null (ps->addr);
675 GNUNET_free (ps->url);
676 if (ps->msgtok != NULL)
677 GNUNET_SERVER_mst_destroy (ps->msgtok);
679 msg = ps->pending_msgs_head;
686 if (ps->direction == OUTBOUND)
688 if (ps->send_endpoint != NULL)
689 curl_easy_cleanup (ps->send_endpoint);
690 if (ps->recv_endpoint != NULL)
691 curl_easy_cleanup (ps->recv_endpoint);
697 GNUNET_STATISTICS_update (plugin->env->stats,
698 gettext_noop ("# HTTP peers active"),
705 * Removes a session from the linked list of sessions
706 * @param pc peer context
708 * @param call_msg_cont GNUNET_YES to call pending message continuations, otherwise no
709 * @param call_msg_cont_result result to call message continuations with
710 * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
713 remove_session (struct HTTP_PeerContext *pc,
714 struct Session *ps, int call_msg_cont, int call_msg_cont_result)
716 struct HTTP_Message *msg;
717 struct Plugin *plugin = ps->peercontext->plugin;
719 #if DEBUG_CONNECTIONS
720 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
721 "Connection %X: removing %s session %X with id %u\n",
723 (ps->direction == INBOUND)
724 ? "inbound" : "outbound", ps, ps->session_id);
726 plugin->env->session_end (plugin, &pc->identity, ps);
727 GNUNET_free_non_null (ps->addr);
728 GNUNET_SERVER_mst_destroy (ps->msgtok);
729 GNUNET_free (ps->url);
730 if (ps->direction == INBOUND)
732 if (ps->recv_endpoint != NULL)
734 curl_easy_cleanup (ps->recv_endpoint);
735 ps->recv_endpoint = NULL;
737 if (ps->send_endpoint != NULL)
739 curl_easy_cleanup (ps->send_endpoint);
740 ps->send_endpoint = NULL;
744 msg = ps->pending_msgs_head;
747 if ((call_msg_cont == GNUNET_YES) && (msg->transmit_cont != NULL))
749 msg->transmit_cont (msg->transmit_cont_cls,
750 &pc->identity, call_msg_cont_result);
752 GNUNET_CONTAINER_DLL_remove (ps->pending_msgs_head,
753 ps->pending_msgs_head, msg);
755 msg = ps->pending_msgs_head;
758 GNUNET_CONTAINER_DLL_remove (pc->head, pc->tail, ps);
762 /* no sessions left remove peer */
763 if (pc->head == NULL)
766 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
767 "No sessions left for peer `%s', removing context\n",
768 GNUNET_i2s (&pc->identity));
770 remove_peer_context_Iterator (plugin, &pc->identity.hashPubKey, pc);
779 check_localaddress (const struct sockaddr *addr, socklen_t addrlen)
782 int local = GNUNET_NO;
783 int af = addr->sa_family;
789 uint32_t netmask = 0x7F000000;
790 uint32_t address = ntohl (((struct sockaddr_in *) addr)->sin_addr.s_addr);
792 res = (address >> 24) ^ (netmask >> 24);
798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799 "Checking IPv4 address `%s': %s\n", GNUNET_a2s (addr, addrlen),
800 (local == GNUNET_YES) ? "local" : "global");
806 if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr) ||
807 IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
812 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
813 "Checking IPv6 address `%s' : %s\n", GNUNET_a2s (addr, addrlen),
814 (local == GNUNET_YES) ? "local" : "global");
824 * Add the IP of our network interface to the list of
825 * our external IP addresses.
827 * @param cls the 'struct Plugin*'
828 * @param name name of the interface
829 * @param isDefault do we think this may be our default interface
830 * @param addr address of the interface
831 * @param addrlen number of bytes in addr
832 * @return GNUNET_OK to continue iterating
835 process_interfaces (void *cls,
838 const struct sockaddr *addr, socklen_t addrlen)
840 struct Plugin *plugin = cls;
841 struct IPv4HttpAddress *t4;
842 struct IPv6HttpAddress *t6;
845 if (plugin->use_localaddresses == GNUNET_NO)
847 if (GNUNET_YES == check_localaddress (addr, addrlen))
850 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
852 "Not notifying transport of address `%s' (local address)\n",
853 GNUNET_a2s (addr, addrlen));
860 GNUNET_assert (cls != NULL);
861 af = addr->sa_family;
862 if ((af == AF_INET) &&
863 (plugin->use_ipv4 == GNUNET_YES) && (plugin->bind6_address == NULL))
866 struct in_addr bnd_cmp = ((struct sockaddr_in *) addr)->sin_addr;
868 t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddress));
869 // Not skipping loopback addresses
872 t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
873 t4->port = htons (plugin->port_inbound);
874 if (plugin->bind4_address != NULL)
877 memcmp (&plugin->bind4_address->sin_addr, &bnd_cmp,
878 sizeof (struct in_addr)))
880 GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head,
881 plugin->ipv4_addr_tail, t4);
882 plugin->env->notify_address (plugin->env->cls,
884 t4, sizeof (struct IPv4HttpAddress));
892 GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head,
893 plugin->ipv4_addr_tail, t4);
894 plugin->env->notify_address (plugin->env->cls,
896 t4, sizeof (struct IPv4HttpAddress));
900 if ((af == AF_INET6) &&
901 (plugin->use_ipv6 == GNUNET_YES) && (plugin->bind4_address == NULL))
904 struct in6_addr bnd_cmp6 = ((struct sockaddr_in6 *) addr)->sin6_addr;
906 t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddress));
907 GNUNET_assert (t6 != NULL);
909 if (plugin->bind6_address != NULL)
911 if (0 == memcmp (&plugin->bind6_address->sin6_addr,
912 &bnd_cmp6, sizeof (struct in6_addr)))
914 memcpy (&t6->ipv6_addr,
915 &((struct sockaddr_in6 *) addr)->sin6_addr,
916 sizeof (struct in6_addr));
917 t6->port = htons (plugin->port_inbound);
918 plugin->env->notify_address (plugin->env->cls,
920 t6, sizeof (struct IPv6HttpAddress));
921 GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head,
922 plugin->ipv6_addr_tail, t6);
928 memcpy (&t6->ipv6_addr,
929 &((struct sockaddr_in6 *) addr)->sin6_addr,
930 sizeof (struct in6_addr));
931 t6->port = htons (plugin->port_inbound);
932 GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head, plugin->ipv6_addr_tail,
935 plugin->env->notify_address (plugin->env->cls,
937 t6, sizeof (struct IPv6HttpAddress));
944 * External logging function for MHD
945 * @param arg arguments
946 * @param fmt format string
947 * @param ap list of arguments
950 mhd_logger (void *arg, const char *fmt, va_list ap)
954 vsnprintf (text, sizeof (text), fmt, ap);
956 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MHD: %s\n", text);
961 mhd_termination_cb (void *cls,
962 struct MHD_Connection *connection, void **httpSessionCache)
964 struct Session *ps = *httpSessionCache;
968 struct HTTP_PeerContext *pc = ps->peercontext;
969 struct Plugin *plugin = cls;
971 GNUNET_assert (cls != NULL);
972 plugin->current_connections--;
974 if (connection == ps->recv_endpoint)
976 #if DEBUG_CONNECTIONS
977 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
978 "Connection %X: inbound connection from peer `%s' was terminated\n",
979 ps, GNUNET_i2s (&pc->identity));
981 ps->recv_active = GNUNET_NO;
982 ps->recv_connected = GNUNET_NO;
983 ps->recv_endpoint = NULL;
985 if (connection == ps->send_endpoint)
987 ps->send_active = GNUNET_NO;
988 ps->send_connected = GNUNET_NO;
989 ps->send_endpoint = NULL;
990 #if DEBUG_CONNECTIONS
991 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
992 "Connection %X: outbound connection from peer `%s' was terminated\n",
993 ps, GNUNET_i2s (&pc->identity));
997 /* if both connections disconnected, remove session */
998 if ((ps->send_connected == GNUNET_NO) && (ps->recv_connected == GNUNET_NO))
1000 GNUNET_STATISTICS_update (pc->plugin->env->stats,
1002 ("# HTTP inbound sessions for peers active"), -1,
1004 remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR);
1010 * Callback called by MessageStreamTokenizer when a message has arrived
1011 * @param cls current session as closure
1012 * @param client clien
1013 * @param message the message to be forwarded to transport service
1016 mhd_write_mst_cb (void *cls,
1017 void *client, const struct GNUNET_MessageHeader *message)
1019 struct Session *ps = cls;
1020 struct HTTP_PeerContext *pc;
1021 struct GNUNET_TIME_Relative delay;
1023 GNUNET_assert (ps != NULL);
1024 pc = ps->peercontext;
1025 GNUNET_assert (pc != NULL);
1027 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1028 "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
1030 ntohs (message->type),
1031 ntohs (message->size),
1032 GNUNET_i2s (&(ps->peercontext)->identity),
1033 http_plugin_address_to_string (NULL, ps->addr, ps->addrlen));
1035 struct GNUNET_TRANSPORT_ATS_Information distance[2];
1037 distance[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
1038 distance[0].value = htonl (1);
1039 distance[1].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
1040 distance[1].value = htonl (0);
1041 delay = pc->plugin->env->receive (ps->peercontext->plugin->env->cls,
1045 GNUNET_TRANSPORT_ATS_Information *)
1046 &distance, 2, ps, NULL, 0);
1048 if (pc->reset_task != GNUNET_SCHEDULER_NO_TASK)
1049 GNUNET_SCHEDULER_cancel (pc->reset_task);
1051 if (delay.rel_value > 0)
1054 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1055 "Connection %X: Inbound quota management: delay next read for %llu ms \n",
1056 ps, delay.rel_value);
1059 GNUNET_SCHEDULER_add_delayed (delay, &reset_inbound_quota_delay, pc);
1065 * Check if incoming connection is accepted.
1066 * NOTE: Here every connection is accepted
1067 * @param cls plugin as closure
1068 * @param addr address of incoming connection
1069 * @param addr_len address length of incoming connection
1070 * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
1074 mhd_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
1076 struct Plugin *plugin = cls;
1078 GNUNET_assert (cls != NULL);
1080 if (plugin->max_connect_per_transport > plugin->current_connections)
1082 plugin->current_connections++;
1091 * Callback called by MHD when it needs data to send
1092 * @param cls current session
1093 * @param pos position in buffer
1094 * @param buf the buffer to write data to
1095 * @param max max number of bytes available in buffer
1096 * @return bytes written to buffer
1099 mhd_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
1101 struct Session *ps = cls;
1102 struct HTTP_PeerContext *pc;
1103 struct HTTP_Message *msg;
1106 GNUNET_assert (ps != NULL);
1108 pc = ps->peercontext;
1109 msg = ps->pending_msgs_tail;
1110 if (ps->send_force_disconnect == GNUNET_YES)
1112 #if DEBUG_CONNECTIONS
1113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1114 "Connection %X: outbound forced to disconnect\n", ps);
1122 if ((msg->size - msg->pos) <= max)
1124 memcpy (buf, &msg->buf[msg->pos], (msg->size - msg->pos));
1125 bytes_read = msg->size - msg->pos;
1126 msg->pos += (msg->size - msg->pos);
1130 memcpy (buf, &msg->buf[msg->pos], max);
1135 /* removing message */
1136 if (msg->pos == msg->size)
1138 if (NULL != msg->transmit_cont)
1139 msg->transmit_cont (msg->transmit_cont_cls, &pc->identity, GNUNET_OK);
1140 ps->queue_length_cur -= msg->size;
1141 remove_http_message (ps, msg);
1144 #if DEBUG_CONNECTIONS
1145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1146 "Connection %X: MHD has sent %u bytes\n", ps, bytes_read);
1153 * Process GET or PUT request received via MHD. For
1154 * GET, queue response that will send back our pending
1155 * messages. For PUT, process incoming data and send
1156 * to GNUnet core. In either case, check if a session
1157 * already exists and create a new one if not.
1160 mhd_access_cb (void *cls,
1161 struct MHD_Connection *mhd_connection,
1164 const char *version,
1165 const char *upload_data,
1166 size_t * upload_data_size, void **httpSessionCache)
1168 struct Plugin *plugin = cls;
1169 struct MHD_Response *response;
1170 const union MHD_ConnectionInfo *conn_info;
1171 const struct sockaddr *client_addr;
1172 const struct sockaddr_in *addrin;
1173 const struct sockaddr_in6 *addrin6;
1174 char address[INET6_ADDRSTRLEN + 14];
1175 struct GNUNET_PeerIdentity pi_in;
1177 struct IPv4HttpAddress ipv4addr;
1178 struct IPv6HttpAddress ipv6addr;
1179 struct HTTP_PeerContext *pc = NULL;
1180 struct Session *ps = NULL;
1181 struct Session *ps_tmp = NULL;
1182 int res = GNUNET_NO;
1184 size_t addr_len = 0;
1186 GNUNET_assert (cls != NULL);
1188 if (NULL == *httpSessionCache)
1190 /* check url for peer identity , if invalid send HTTP 404 */
1191 size_t len = strlen (&url[1]);
1192 char *peer = GNUNET_malloc (104 + 1);
1194 if ((len > 104) && (url[104] == ';'))
1196 char *id = GNUNET_malloc ((len - 104) + 1);
1198 strcpy (id, &url[105]);
1199 memcpy (peer, &url[1], 103);
1201 id_num = strtoul (id, NULL, 10);
1204 res = GNUNET_CRYPTO_hash_from_string (peer, &(pi_in.hashPubKey));
1206 if (GNUNET_SYSERR == res)
1208 response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),
1209 HTTP_ERROR_RESPONSE,
1211 res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
1212 MHD_destroy_response (response);
1213 #if DEBUG_CONNECTIONS
1215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1216 "Peer has no valid ident, sent HTTP 1.1/404\n");
1218 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1219 "Peer has no valid ident, could not send error\n");
1226 ps = *httpSessionCache;
1227 pc = ps->peercontext;
1230 if (NULL == *httpSessionCache)
1232 /* get peer context */
1233 pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &pi_in.hashPubKey);
1237 pc = GNUNET_malloc (sizeof (struct HTTP_PeerContext));
1238 pc->plugin = plugin;
1239 pc->session_id_counter = 1;
1240 pc->last_session = NULL;
1241 memcpy (&pc->identity, &pi_in, sizeof (struct GNUNET_PeerIdentity));
1242 GNUNET_CONTAINER_multihashmap_put (plugin->peers,
1243 &pc->identity.hashPubKey,
1245 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1246 GNUNET_STATISTICS_update (plugin->env->stats,
1247 gettext_noop ("# HTTP peers active"),
1252 MHD_get_connection_info (mhd_connection,
1253 MHD_CONNECTION_INFO_CLIENT_ADDRESS);
1254 /* Incoming IPv4 connection */
1255 /* cast required for legacy MHD API < 0.9.6 */
1256 client_addr = (const struct sockaddr *) conn_info->client_addr;
1257 if (AF_INET == client_addr->sa_family)
1259 addrin = (const struct sockaddr_in *) client_addr;
1260 inet_ntop (addrin->sin_family, &(addrin->sin_addr), address,
1262 memcpy (&ipv4addr.ipv4_addr, &(addrin->sin_addr),
1263 sizeof (struct in_addr));
1264 ipv4addr.port = addrin->sin_port;
1266 addr_len = sizeof (struct IPv4HttpAddress);
1268 /* Incoming IPv6 connection */
1269 if (AF_INET6 == client_addr->sa_family)
1271 addrin6 = (const struct sockaddr_in6 *) client_addr;
1272 inet_ntop (addrin6->sin6_family, &(addrin6->sin6_addr), address,
1274 memcpy (&ipv6addr.ipv6_addr, &(addrin6->sin6_addr),
1275 sizeof (struct in6_addr));
1276 ipv6addr.port = addrin6->sin6_port;
1278 addr_len = sizeof (struct IPv6HttpAddress);
1281 GNUNET_assert (addr != NULL);
1282 GNUNET_assert (addr_len != 0);
1285 /* only inbound sessions here */
1288 while (ps_tmp != NULL)
1290 if ((ps_tmp->direction == INBOUND) && (ps_tmp->session_id == id_num) &&
1293 if ((ps_tmp->recv_force_disconnect != GNUNET_YES) &&
1294 (ps_tmp->send_force_disconnect != GNUNET_YES))
1298 ps_tmp = ps_tmp->next;
1303 ps = GNUNET_malloc (sizeof (struct Session));
1304 ps->addr = GNUNET_malloc (addr_len);
1305 memcpy (ps->addr, addr, addr_len);
1306 ps->addrlen = addr_len;
1307 ps->direction = INBOUND;
1308 ps->pending_msgs_head = NULL;
1309 ps->pending_msgs_tail = NULL;
1310 ps->send_connected = GNUNET_NO;
1311 ps->send_active = GNUNET_NO;
1312 ps->recv_connected = GNUNET_NO;
1313 ps->recv_active = GNUNET_NO;
1314 ps->peercontext = pc;
1315 ps->session_id = id_num;
1316 ps->queue_length_cur = 0;
1317 ps->queue_length_max = GNUNET_SERVER_MAX_MESSAGE_SIZE;
1318 ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
1319 GNUNET_CONTAINER_DLL_insert (pc->head, pc->tail, ps);
1320 GNUNET_STATISTICS_update (plugin->env->stats,
1322 ("# HTTP inbound sessions for peers active"), 1,
1326 *httpSessionCache = ps;
1327 if (ps->msgtok == NULL)
1328 ps->msgtok = GNUNET_SERVER_mst_create (&mhd_write_mst_cb, ps);
1330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1331 "Connection %X: HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
1334 GNUNET_i2s (&pc->identity),
1335 http_plugin_address_to_string (NULL, ps->addr, ps->addrlen));
1339 /* Is it a PUT or a GET request */
1340 if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
1342 if (ps->recv_force_disconnect == GNUNET_YES)
1344 #if DEBUG_CONNECTIONS
1345 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1346 "Connection %X: inbound connection was forced to disconnect\n",
1349 ps->recv_active = GNUNET_NO;
1352 if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_NO))
1354 ps->recv_endpoint = mhd_connection;
1355 ps->recv_connected = GNUNET_YES;
1356 ps->recv_active = GNUNET_YES;
1357 ps->recv_force_disconnect = GNUNET_NO;
1358 #if DEBUG_CONNECTIONS
1359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1360 "Connection %X: inbound PUT connection connected\n", ps);
1365 /* Transmission of all data complete */
1366 if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_YES))
1368 response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),
1371 res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1372 #if DEBUG_CONNECTIONS
1373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1374 "Connection %X: Sent HTTP/1.1: 200 OK as PUT Response\n", ps);
1376 MHD_destroy_response (response);
1377 ps->recv_active = GNUNET_NO;
1381 /* Recieving data */
1382 if ((*upload_data_size > 0) && (ps->recv_active == GNUNET_YES))
1384 if (pc->delay.rel_value == 0)
1387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1388 "Connection %X: PUT with %u bytes forwarded to MST\n",
1389 ps, *upload_data_size);
1391 res = GNUNET_SERVER_mst_receive (ps->msgtok, ps,
1392 upload_data, *upload_data_size,
1393 GNUNET_NO, GNUNET_NO);
1394 (*upload_data_size) = 0;
1399 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1400 "Connection %X: no inbound bandwidth available! Next read was delayed for %llu ms\n",
1401 ps, ps->peercontext->delay.rel_value);
1409 if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
1411 if (ps->send_force_disconnect == GNUNET_YES)
1413 #if DEBUG_CONNECTIONS
1414 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1415 "Connection %X: outbound connection was forced to disconnect\n",
1418 ps->send_active = GNUNET_NO;
1421 ps->send_connected = GNUNET_YES;
1422 ps->send_active = GNUNET_YES;
1423 ps->send_endpoint = mhd_connection;
1424 ps->send_force_disconnect = GNUNET_NO;
1425 #if DEBUG_CONNECTIONS
1426 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1427 "Connection %X: inbound GET connection connected\n", ps);
1430 MHD_create_response_from_callback (-1, 32 * 1024, &mhd_send_callback,
1432 res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1433 MHD_destroy_response (response);
1441 * Function that queries MHD's select sets and
1442 * starts the task waiting for them.
1443 * @param plugin plugin
1444 * @param daemon_handle the MHD daemon handle
1445 * @return gnunet task identifier
1447 static GNUNET_SCHEDULER_TaskIdentifier
1448 http_server_daemon_prepare (struct Plugin *plugin,
1449 struct MHD_Daemon *daemon_handle)
1451 GNUNET_SCHEDULER_TaskIdentifier ret;
1455 struct GNUNET_NETWORK_FDSet *wrs;
1456 struct GNUNET_NETWORK_FDSet *wws;
1457 struct GNUNET_NETWORK_FDSet *wes;
1459 unsigned long long timeout;
1461 struct GNUNET_TIME_Relative tv;
1463 ret = GNUNET_SCHEDULER_NO_TASK;
1467 wrs = GNUNET_NETWORK_fdset_create ();
1468 wes = GNUNET_NETWORK_fdset_create ();
1469 wws = GNUNET_NETWORK_fdset_create ();
1471 GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
1472 haveto = MHD_get_timeout (daemon_handle, &timeout);
1473 if (haveto == MHD_YES)
1474 tv.rel_value = (uint64_t) timeout;
1476 tv = GNUNET_TIME_UNIT_SECONDS;
1477 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1478 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1479 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1480 if (daemon_handle == plugin->http_server_daemon_v4)
1482 if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1484 GNUNET_SCHEDULER_cancel (plugin->http_server_task_v4);
1485 plugin->http_server_daemon_v4 = GNUNET_SCHEDULER_NO_TASK;
1488 ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1489 GNUNET_SCHEDULER_NO_TASK,
1492 wws, &http_server_daemon_v4_run, plugin);
1494 if (daemon_handle == plugin->http_server_daemon_v6)
1496 if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1498 GNUNET_SCHEDULER_cancel (plugin->http_server_task_v6);
1499 plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1502 ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1503 GNUNET_SCHEDULER_NO_TASK,
1506 wws, &http_server_daemon_v6_run, plugin);
1508 GNUNET_NETWORK_fdset_destroy (wrs);
1509 GNUNET_NETWORK_fdset_destroy (wws);
1510 GNUNET_NETWORK_fdset_destroy (wes);
1516 * Call MHD IPv4 to process pending requests and then go back
1517 * and schedule the next run.
1518 * @param cls plugin as closure
1519 * @param tc task context
1522 http_server_daemon_v4_run (void *cls,
1523 const struct GNUNET_SCHEDULER_TaskContext *tc)
1525 struct Plugin *plugin = cls;
1527 #if DEBUG_SCHEDULING
1528 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
1529 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1530 "http_server_daemon_v4_run: GNUNET_SCHEDULER_REASON_READ_READY\n");
1531 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
1532 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1533 "http_server_daemon_v4_run: GNUNET_SCHEDULER_REASON_WRITE_READY\n");
1534 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
1535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1536 "http_server_daemon_v4_run: GNUNET_SCHEDULER_REASON_TIMEOUT\n");
1537 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_STARTUP))
1538 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1539 "http_server_daemon_v4_run: GGNUNET_SCHEDULER_REASON_STARTUP\n");
1540 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1541 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1542 "http_server_daemon_v4_run: GGNUNET_SCHEDULER_REASON_SHUTDOWN\n");
1545 GNUNET_assert (cls != NULL);
1546 plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1548 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1551 GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4));
1552 plugin->http_server_task_v4 =
1553 http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
1558 * Call MHD IPv6 to process pending requests and then go back
1559 * and schedule the next run.
1560 * @param cls plugin as closure
1561 * @param tc task context
1564 http_server_daemon_v6_run (void *cls,
1565 const struct GNUNET_SCHEDULER_TaskContext *tc)
1567 struct Plugin *plugin = cls;
1569 #if DEBUG_SCHEDULING
1570 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
1571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1572 "http_server_daemon_v6_run: GNUNET_SCHEDULER_REASON_READ_READY\n");
1573 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
1574 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1575 "http_server_daemon_v6_run: GNUNET_SCHEDULER_REASON_WRITE_READY\n");
1576 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
1577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1578 "http_server_daemon_v6_run: GNUNET_SCHEDULER_REASON_TIMEOUT\n");
1579 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_STARTUP))
1580 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1581 "http_server_daemon_v6_run: GGNUNET_SCHEDULER_REASON_STARTUP\n");
1582 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1583 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1584 "http_server_daemon_v6_run: GGNUNET_SCHEDULER_REASON_SHUTDOWN\n");
1587 GNUNET_assert (cls != NULL);
1588 plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1590 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1593 GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6));
1594 plugin->http_server_task_v6 =
1595 http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
1600 curl_get_header_cb (void *ptr, size_t size, size_t nmemb, void *stream)
1602 struct Session *ps = stream;
1604 long http_result = 0;
1607 /* Getting last http result code */
1608 GNUNET_assert (NULL != ps);
1609 if (ps->recv_connected == GNUNET_NO)
1612 curl_easy_getinfo (ps->recv_endpoint, CURLINFO_RESPONSE_CODE,
1614 if (CURLE_OK == res)
1616 if (http_result == 200)
1618 ps->recv_connected = GNUNET_YES;
1619 ps->recv_active = GNUNET_YES;
1620 #if DEBUG_CONNECTIONS
1621 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1622 "Connection %X: connected to recieve data\n", ps);
1624 // Calling send_check_connections again since receive is established
1625 send_check_connections (ps->peercontext->plugin, ps);
1632 size_t len = size * nmemb;
1635 if ((size * nmemb) < SIZE_MAX)
1636 tmp = GNUNET_malloc (len + 1);
1638 if ((tmp != NULL) && (len > 0))
1640 memcpy (tmp, ptr, len);
1643 if (tmp[len - 2] == 13)
1644 tmp[len - 2] = '\0';
1646 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1647 "Connection %X: Header: %s\n", ps, tmp);
1649 GNUNET_free_non_null (tmp);
1652 return size * nmemb;
1657 * Callback called by libcurl when new headers arrive
1658 * Used to get HTTP result for curl operations
1659 * @param ptr stream to read from
1660 * @param size size of one char element
1661 * @param nmemb number of char elements
1662 * @param stream closure set by user
1663 * @return bytes read by function
1666 curl_put_header_cb (void *ptr, size_t size, size_t nmemb, void *stream)
1668 struct Session *ps = stream;
1671 size_t len = size * nmemb;
1672 long http_result = 0;
1675 /* Getting last http result code */
1676 GNUNET_assert (NULL != ps);
1678 curl_easy_getinfo (ps->send_endpoint, CURLINFO_RESPONSE_CODE,
1680 if (CURLE_OK == res)
1682 if ((http_result == 100) && (ps->send_connected == GNUNET_NO))
1684 ps->send_connected = GNUNET_YES;
1685 ps->send_active = GNUNET_YES;
1686 #if DEBUG_CONNECTIONS
1687 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1688 "Connection %X: connected to send data\n", ps);
1691 if ((http_result == 200) && (ps->send_connected == GNUNET_YES))
1693 ps->send_connected = GNUNET_NO;
1694 ps->send_active = GNUNET_NO;
1695 #if DEBUG_CONNECTIONS
1696 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1697 "Connection %X: sending disconnected\n", ps);
1703 if ((size * nmemb) < SIZE_MAX)
1704 tmp = GNUNET_malloc (len + 1);
1706 if ((tmp != NULL) && (len > 0))
1708 memcpy (tmp, ptr, len);
1711 if (tmp[len - 2] == 13)
1712 tmp[len - 2] = '\0';
1715 GNUNET_free_non_null (tmp);
1716 return size * nmemb;
1720 * Callback method used with libcurl
1721 * Method is called when libcurl needs to read data during sending
1722 * @param stream pointer where to write data
1723 * @param size size of an individual element
1724 * @param nmemb count of elements that can be written to the buffer
1725 * @param ptr source pointer, passed to the libcurl handle
1726 * @return bytes written to stream
1729 curl_send_cb (void *stream, size_t size, size_t nmemb, void *ptr)
1731 struct Session *ps = ptr;
1732 struct HTTP_Message *msg = ps->pending_msgs_tail;
1736 if (ps->send_active == GNUNET_NO)
1737 return CURL_READFUNC_PAUSE;
1738 if ((ps->pending_msgs_tail == NULL) && (ps->send_active == GNUNET_YES))
1740 #if DEBUG_CONNECTIONS
1741 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1742 "Connection %X: No Message to send, pausing connection\n", ps);
1744 ps->send_active = GNUNET_NO;
1745 return CURL_READFUNC_PAUSE;
1748 GNUNET_assert (msg != NULL);
1751 if (msg->pos < msg->size)
1753 /* data fit in buffer */
1754 if ((msg->size - msg->pos) <= (size * nmemb))
1756 len = (msg->size - msg->pos);
1757 memcpy (stream, &msg->buf[msg->pos], len);
1764 memcpy (stream, &msg->buf[msg->pos], len);
1769 /* no data to send */
1775 if (msg->pos == msg->size)
1777 #if DEBUG_CONNECTIONS
1778 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1779 "Connection %X: Message with %u bytes sent, removing message from queue\n",
1782 /* Calling transmit continuation */
1783 if (NULL != ps->pending_msgs_tail->transmit_cont)
1784 msg->transmit_cont (ps->pending_msgs_tail->transmit_cont_cls,
1785 &(ps->peercontext)->identity, GNUNET_OK);
1786 ps->queue_length_cur -= msg->size;
1787 remove_http_message (ps, msg);
1794 curl_receive_mst_cb (void *cls,
1795 void *client, const struct GNUNET_MessageHeader *message)
1797 struct Session *ps = cls;
1798 struct GNUNET_TIME_Relative delay;
1800 GNUNET_assert (ps != NULL);
1802 struct HTTP_PeerContext *pc = ps->peercontext;
1804 GNUNET_assert (pc != NULL);
1806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1807 "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
1809 ntohs (message->type),
1810 ntohs (message->size),
1811 GNUNET_i2s (&(pc->identity)),
1812 http_plugin_address_to_string (NULL, ps->addr, ps->addrlen));
1814 struct GNUNET_TRANSPORT_ATS_Information distance[2];
1816 distance[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
1817 distance[0].value = htonl (1);
1818 distance[1].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
1819 distance[1].value = htonl (0);
1821 delay = pc->plugin->env->receive (pc->plugin->env->cls,
1825 GNUNET_TRANSPORT_ATS_Information *)
1826 &distance, 2, ps, ps->addr, ps->addrlen);
1828 if (pc->reset_task != GNUNET_SCHEDULER_NO_TASK)
1829 GNUNET_SCHEDULER_cancel (pc->reset_task);
1831 if (delay.rel_value > 0)
1834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1835 "Connection %X: Inbound quota management: delay next read for %llu ms\n",
1836 ps, delay.rel_value);
1839 GNUNET_SCHEDULER_add_delayed (delay, &reset_inbound_quota_delay, pc);
1845 * Callback method used with libcurl
1846 * Method is called when libcurl needs to write data during sending
1847 * @param stream pointer where to write data
1848 * @param size size of an individual element
1849 * @param nmemb count of elements that can be written to the buffer
1850 * @param ptr destination pointer, passed to the libcurl handle
1851 * @return bytes read from stream
1854 curl_receive_cb (void *stream, size_t size, size_t nmemb, void *ptr)
1856 struct Session *ps = ptr;
1858 if (ps->peercontext->delay.rel_value > 0)
1861 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1862 "Connection %X: no inbound bandwidth available! Next read was delayed for %llu ms\n",
1863 ps, ps->peercontext->delay.rel_value);
1867 #if DEBUG_CONNECTIONS
1868 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1869 "Connection %X: %u bytes received\n", ps, size * nmemb);
1871 GNUNET_SERVER_mst_receive (ps->msgtok, ps,
1872 stream, size * nmemb, GNUNET_NO, GNUNET_NO);
1873 return (size * nmemb);
1878 curl_handle_finished (struct Plugin *plugin)
1880 struct Session *ps = NULL;
1881 struct HTTP_PeerContext *pc = NULL;
1882 struct CURLMsg *msg;
1883 struct HTTP_Message *cur_msg = NULL;
1890 msg = curl_multi_info_read (plugin->multi_handle, &msgs_in_queue);
1891 if ((msgs_in_queue == 0) || (msg == NULL))
1893 /* get session for affected curl handle */
1894 GNUNET_assert (msg->easy_handle != NULL);
1895 curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, &tmp);
1896 ps = (struct Session *) tmp;
1897 GNUNET_assert (ps != NULL);
1898 pc = ps->peercontext;
1899 GNUNET_assert (pc != NULL);
1903 if ((msg->data.result != CURLE_OK) &&
1904 (msg->data.result != CURLE_GOT_NOTHING))
1906 /* sending msg failed */
1907 if (msg->easy_handle == ps->send_endpoint)
1909 #if DEBUG_CONNECTIONS
1910 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1912 ("Connection %X: HTTP PUT to peer `%s' (`%s') failed: `%s' `%s'\n"),
1913 ps, GNUNET_i2s (&pc->identity),
1914 http_plugin_address_to_string (NULL, ps->addr,
1916 "curl_multi_perform",
1917 curl_easy_strerror (msg->data.result));
1919 ps->send_connected = GNUNET_NO;
1920 ps->send_active = GNUNET_NO;
1921 curl_multi_remove_handle (plugin->multi_handle, ps->send_endpoint);
1922 while (ps->pending_msgs_tail != NULL)
1924 cur_msg = ps->pending_msgs_tail;
1925 if (NULL != cur_msg->transmit_cont)
1926 cur_msg->transmit_cont (cur_msg->transmit_cont_cls, &pc->identity,
1928 ps->queue_length_cur -= cur_msg->size;
1929 remove_http_message (ps, cur_msg);
1932 /* GET connection failed */
1933 if (msg->easy_handle == ps->recv_endpoint)
1935 #if DEBUG_CONNECTIONS
1936 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1938 ("Connection %X: HTTP GET to peer `%s' (`%s') failed: `%s' `%s'\n"),
1939 ps, GNUNET_i2s (&pc->identity),
1940 http_plugin_address_to_string (NULL, ps->addr,
1942 "curl_multi_perform",
1943 curl_easy_strerror (msg->data.result));
1945 ps->recv_connected = GNUNET_NO;
1946 ps->recv_active = GNUNET_NO;
1947 curl_multi_remove_handle (plugin->multi_handle, ps->recv_endpoint);
1952 if (msg->easy_handle == ps->send_endpoint)
1954 GNUNET_assert (CURLE_OK ==
1955 curl_easy_getinfo (msg->easy_handle,
1956 CURLINFO_RESPONSE_CODE,
1958 #if DEBUG_CONNECTIONS
1959 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1960 "Connection %X: HTTP PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1962 GNUNET_i2s (&pc->identity),
1963 http_plugin_address_to_string (NULL, ps->addr,
1964 ps->addrlen), http_result);
1966 /* Calling transmit continuation */
1967 while (ps->pending_msgs_tail != NULL)
1969 cur_msg = ps->pending_msgs_tail;
1970 if (NULL != cur_msg->transmit_cont)
1972 /* HTTP 1xx : Last message before here was informational */
1973 if ((http_result >= 100) && (http_result < 200))
1974 cur_msg->transmit_cont (cur_msg->transmit_cont_cls,
1975 &pc->identity, GNUNET_OK);
1976 /* HTTP 2xx: successful operations */
1977 if ((http_result >= 200) && (http_result < 300))
1978 cur_msg->transmit_cont (cur_msg->transmit_cont_cls,
1979 &pc->identity, GNUNET_OK);
1980 /* HTTP 3xx..5xx: error */
1981 if ((http_result >= 300) && (http_result < 600))
1982 cur_msg->transmit_cont (cur_msg->transmit_cont_cls,
1983 &pc->identity, GNUNET_SYSERR);
1985 ps->queue_length_cur -= cur_msg->size;
1986 remove_http_message (ps, cur_msg);
1989 ps->send_connected = GNUNET_NO;
1990 ps->send_active = GNUNET_NO;
1991 curl_multi_remove_handle (plugin->multi_handle, ps->send_endpoint);
1993 if (msg->easy_handle == ps->recv_endpoint)
1995 #if DEBUG_CONNECTIONS
1996 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1997 "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1999 GNUNET_i2s (&pc->identity),
2000 http_plugin_address_to_string (NULL, ps->addr,
2001 ps->addrlen), http_result);
2003 ps->recv_connected = GNUNET_NO;
2004 ps->recv_active = GNUNET_NO;
2005 curl_multi_remove_handle (plugin->multi_handle, ps->recv_endpoint);
2007 plugin->current_connections--;
2009 if ((ps->recv_connected == GNUNET_NO) &&
2010 (ps->send_connected == GNUNET_NO))
2011 remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR);
2017 while ((msgs_in_queue > 0));
2022 * Task performing curl operations
2023 * @param cls plugin as closure
2024 * @param tc gnunet scheduler task context
2027 curl_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2029 struct Plugin *plugin = cls;
2030 static unsigned int handles_last_run;
2034 GNUNET_assert (cls != NULL);
2036 plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2037 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2042 mret = curl_multi_perform (plugin->multi_handle, &running);
2043 if ((running < handles_last_run) && (running > 0))
2044 curl_handle_finished (plugin);
2045 handles_last_run = running;
2047 while (mret == CURLM_CALL_MULTI_PERFORM);
2048 curl_schedule (plugin);
2053 * Function setting up file descriptors and scheduling task to run
2055 * @param plugin plugin as closure
2056 * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
2059 curl_schedule (struct Plugin *plugin)
2065 struct GNUNET_NETWORK_FDSet *grs;
2066 struct GNUNET_NETWORK_FDSet *gws;
2070 /* Cancel previous scheduled task */
2071 if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2073 GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
2074 plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2081 mret = curl_multi_fdset (plugin->multi_handle, &rs, &ws, &es, &max);
2082 if (mret != CURLM_OK)
2084 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2085 _("%s failed at %s:%d: `%s'\n"),
2086 "curl_multi_fdset", __FILE__, __LINE__,
2087 curl_multi_strerror (mret));
2088 return GNUNET_SYSERR;
2090 mret = curl_multi_timeout (plugin->multi_handle, &to);
2091 if (mret != CURLM_OK)
2093 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2094 _("%s failed at %s:%d: `%s'\n"),
2095 "curl_multi_timeout", __FILE__, __LINE__,
2096 curl_multi_strerror (mret));
2097 return GNUNET_SYSERR;
2100 grs = GNUNET_NETWORK_fdset_create ();
2101 gws = GNUNET_NETWORK_fdset_create ();
2102 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
2103 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
2104 plugin->http_curl_task =
2105 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
2106 GNUNET_SCHEDULER_NO_TASK,
2109 GNUNET_TIME_relative_multiply
2110 (GNUNET_TIME_UNIT_SECONDS,
2112 GNUNET_TIME_relative_multiply
2113 (GNUNET_TIME_UNIT_MILLISECONDS, to), grs,
2114 gws, &curl_perform, plugin);
2115 GNUNET_NETWORK_fdset_destroy (gws);
2116 GNUNET_NETWORK_fdset_destroy (grs);
2123 * Function to log curl debug messages with GNUNET_log
2124 * @param curl handle
2125 * @param type curl_infotype
2128 * @param cls closure
2132 curl_logger (CURL * curl,
2133 curl_infotype type, char *data, size_t size, void *cls)
2135 if (type == CURLINFO_TEXT)
2137 char text[size + 2];
2139 memcpy (text, data, size);
2140 if (text[size - 1] == '\n')
2145 text[size + 1] = '\0';
2147 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CURL: Connection %X - %s", cls, text);
2155 * Function setting up curl handle and selecting message to send
2157 * @param plugin plugin
2159 * @return GNUNET_SYSERR on failure, GNUNET_NO if connecting, GNUNET_YES if ok
2162 send_check_connections (struct Plugin *plugin, struct Session *ps)
2165 struct GNUNET_TIME_Relative timeout =
2166 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
2168 if ((ps->direction == OUTBOUND) &&
2169 (plugin->current_connections < plugin->max_connect_per_transport))
2171 /* RECV DIRECTION */
2172 /* Check if session is connected to receive data, otherwise connect to peer */
2174 if (ps->recv_connected == GNUNET_NO)
2176 int fresh = GNUNET_NO;
2178 if (ps->recv_endpoint == NULL)
2181 ps->recv_endpoint = curl_easy_init ();
2184 curl_easy_setopt (ps->recv_endpoint, CURLOPT_VERBOSE, 1L);
2185 curl_easy_setopt (ps->recv_endpoint, CURLOPT_DEBUGFUNCTION, &curl_logger);
2186 curl_easy_setopt (ps->recv_endpoint, CURLOPT_DEBUGDATA,
2190 curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSLVERSION,
2191 CURL_SSLVERSION_TLSv1);
2192 curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSL_VERIFYPEER, 0);
2193 curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSL_VERIFYHOST, 0);
2195 curl_easy_setopt (ps->recv_endpoint, CURLOPT_URL, ps->url);
2196 curl_easy_setopt (ps->recv_endpoint, CURLOPT_HEADERFUNCTION,
2197 &curl_get_header_cb);
2198 curl_easy_setopt (ps->recv_endpoint, CURLOPT_WRITEHEADER, ps);
2199 curl_easy_setopt (ps->recv_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
2200 curl_easy_setopt (ps->recv_endpoint, CURLOPT_READDATA, ps);
2201 curl_easy_setopt (ps->recv_endpoint, CURLOPT_WRITEFUNCTION,
2203 curl_easy_setopt (ps->recv_endpoint, CURLOPT_WRITEDATA, ps);
2204 curl_easy_setopt (ps->recv_endpoint, CURLOPT_TIMEOUT,
2205 (long) timeout.rel_value);
2206 curl_easy_setopt (ps->recv_endpoint, CURLOPT_PRIVATE, ps);
2207 curl_easy_setopt (ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT,
2208 HTTP_CONNECT_TIMEOUT);
2209 curl_easy_setopt (ps->recv_endpoint, CURLOPT_BUFFERSIZE,
2210 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
2211 #if CURL_TCP_NODELAY
2212 curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
2214 if (fresh == GNUNET_YES)
2216 mret = curl_multi_add_handle (plugin->multi_handle, ps->recv_endpoint);
2217 if (mret != CURLM_OK)
2219 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2220 _("Connection: %X: %s failed at %s:%d: `%s'\n"),
2222 "curl_multi_add_handle", __FILE__, __LINE__,
2223 curl_multi_strerror (mret));
2224 return GNUNET_SYSERR;
2227 if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2229 GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
2230 plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2232 plugin->current_connections++;
2233 plugin->http_curl_task = GNUNET_SCHEDULER_add_now (&curl_perform, plugin);
2236 /* waiting for receive direction */
2237 if (ps->recv_connected == GNUNET_NO)
2240 /* SEND DIRECTION */
2241 /* Check if session is connected to send data, otherwise connect to peer */
2242 if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint != NULL))
2244 if (ps->send_active == GNUNET_YES)
2246 #if DEBUG_CONNECTIONS
2247 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2248 "Connection %X: outbound active, enqueueing message\n", ps);
2252 if (ps->send_active == GNUNET_NO)
2254 #if DEBUG_CONNECTIONS
2255 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2256 "Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",
2259 if (CURLE_OK == curl_easy_pause (ps->send_endpoint, CURLPAUSE_CONT))
2261 ps->send_active = GNUNET_YES;
2262 if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2264 GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
2265 plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2267 plugin->http_curl_task =
2268 GNUNET_SCHEDULER_add_now (&curl_perform, plugin);
2272 return GNUNET_SYSERR;
2275 /* not connected, initiate connection */
2276 if ((ps->send_connected == GNUNET_NO) &&
2277 (plugin->current_connections < plugin->max_connect_per_transport))
2279 int fresh = GNUNET_NO;
2281 if (NULL == ps->send_endpoint)
2283 ps->send_endpoint = curl_easy_init ();
2286 GNUNET_assert (ps->send_endpoint != NULL);
2287 GNUNET_assert (NULL != ps->pending_msgs_tail);
2288 #if DEBUG_CONNECTIONS
2289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2290 "Connection %X: outbound not connected, initiating connection\n",
2293 ps->send_active = GNUNET_NO;
2296 curl_easy_setopt (ps->send_endpoint, CURLOPT_VERBOSE, 1L);
2297 curl_easy_setopt (ps->send_endpoint, CURLOPT_DEBUGFUNCTION, &curl_logger);
2298 curl_easy_setopt (ps->send_endpoint, CURLOPT_DEBUGDATA,
2302 curl_easy_setopt (ps->send_endpoint, CURLOPT_SSLVERSION,
2303 CURL_SSLVERSION_TLSv1);
2304 curl_easy_setopt (ps->send_endpoint, CURLOPT_SSL_VERIFYPEER, 0);
2305 curl_easy_setopt (ps->send_endpoint, CURLOPT_SSL_VERIFYHOST, 0);
2307 curl_easy_setopt (ps->send_endpoint, CURLOPT_URL, ps->url);
2308 curl_easy_setopt (ps->send_endpoint, CURLOPT_PUT, 1L);
2309 curl_easy_setopt (ps->send_endpoint, CURLOPT_HEADERFUNCTION,
2310 &curl_put_header_cb);
2311 curl_easy_setopt (ps->send_endpoint, CURLOPT_WRITEHEADER, ps);
2312 curl_easy_setopt (ps->send_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
2313 curl_easy_setopt (ps->send_endpoint, CURLOPT_READDATA, ps);
2314 curl_easy_setopt (ps->send_endpoint, CURLOPT_WRITEFUNCTION,
2316 curl_easy_setopt (ps->send_endpoint, CURLOPT_READDATA, ps);
2317 curl_easy_setopt (ps->send_endpoint, CURLOPT_TIMEOUT,
2318 (long) timeout.rel_value);
2319 curl_easy_setopt (ps->send_endpoint, CURLOPT_PRIVATE, ps);
2320 curl_easy_setopt (ps->send_endpoint, CURLOPT_CONNECTTIMEOUT,
2321 HTTP_CONNECT_TIMEOUT);
2322 curl_easy_setopt (ps->send_endpoint, CURLOPT_BUFFERSIZE,
2323 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
2324 #if CURL_TCP_NODELAY
2325 curl_easy_setopt (ps->send_endpoint, CURLOPT_TCP_NODELAY, 1);
2327 if (fresh == GNUNET_YES)
2329 mret = curl_multi_add_handle (plugin->multi_handle, ps->send_endpoint);
2330 if (mret != CURLM_OK)
2332 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2333 _("Connection: %X: %s failed at %s:%d: `%s'\n"),
2335 "curl_multi_add_handle", __FILE__, __LINE__,
2336 curl_multi_strerror (mret));
2337 return GNUNET_SYSERR;
2341 if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2343 GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
2344 plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2346 plugin->current_connections++;
2347 plugin->http_curl_task = GNUNET_SCHEDULER_add_now (&curl_perform, plugin);
2350 if (ps->direction == INBOUND)
2352 GNUNET_assert (NULL != ps->pending_msgs_tail);
2353 if ((ps->recv_connected == GNUNET_YES) && (ps->send_connected == GNUNET_YES)
2354 && (ps->recv_force_disconnect == GNUNET_NO) &&
2355 (ps->recv_force_disconnect == GNUNET_NO))
2358 return GNUNET_SYSERR;
2363 * select best session to transmit data to peer
2365 * @param pc peer context of target peer
2366 * @param addr address of target peer
2367 * @param addrlen address length
2368 * @param force_address does transport service enforce address?
2369 * @param session session passed by transport service
2370 * @return selected session
2373 static struct Session *
2374 send_select_session (struct HTTP_PeerContext *pc,
2375 const void *addr, size_t addrlen,
2376 int force_address, struct Session *session)
2378 struct Session *tmp = NULL;
2379 int addr_given = GNUNET_NO;
2381 if ((addr != NULL) && (addrlen > 0))
2382 addr_given = GNUNET_YES;
2384 if (force_address == GNUNET_YES)
2386 /* check session given as argument */
2387 if ((session != NULL) && (addr_given == GNUNET_YES))
2389 if (0 == memcmp (session->addr, addr, addrlen))
2391 /* connection can not be used, since it is disconnected */
2392 if ((session->recv_force_disconnect == GNUNET_NO) &&
2393 (session->send_force_disconnect == GNUNET_NO))
2395 #if DEBUG_SESSION_SELECTION
2396 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2397 "Session %X selected: Using session passed by transport to send to forced address \n",
2404 /* check last session used */
2405 if ((pc->last_session != NULL) && (addr_given == GNUNET_YES))
2407 if (0 == memcmp (pc->last_session->addr, addr, addrlen))
2409 /* connection can not be used, since it is disconnected */
2410 if ((pc->last_session->recv_force_disconnect == GNUNET_NO) &&
2411 (pc->last_session->send_force_disconnect == GNUNET_NO))
2413 #if DEBUG_SESSION_SELECTION
2414 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2415 "Session %X selected: Using last session used to send to forced address \n",
2418 return pc->last_session;
2422 /* find session in existing sessions */
2424 while ((tmp != NULL) && (addr_given == GNUNET_YES))
2426 if (0 == memcmp (tmp->addr, addr, addrlen))
2428 /* connection can not be used, since it is disconnected */
2429 if ((tmp->recv_force_disconnect == GNUNET_NO) &&
2430 (tmp->send_force_disconnect == GNUNET_NO))
2432 #if DEBUG_SESSION_SELECTION
2433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2434 "Session %X selected: Using existing session to send to forced address \n",
2442 /* no session to use */
2445 if ((force_address == GNUNET_NO) || (force_address == GNUNET_SYSERR))
2447 /* check session given as argument */
2448 if (session != NULL)
2450 /* connection can not be used, since it is disconnected */
2451 if ((session->recv_force_disconnect == GNUNET_NO) &&
2452 (session->send_force_disconnect == GNUNET_NO))
2454 #if DEBUG_SESSION_SELECTION
2455 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2456 "Session %X selected: Using session passed by transport to send not-forced address\n",
2462 /* check last session used */
2463 if (pc->last_session != NULL)
2465 /* connection can not be used, since it is disconnected */
2466 if ((pc->last_session->recv_force_disconnect == GNUNET_NO) &&
2467 (pc->last_session->send_force_disconnect == GNUNET_NO))
2469 #if DEBUG_SESSION_SELECTION
2470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2471 "Session %X selected: Using last session to send to not-forced address\n",
2474 return pc->last_session;
2477 /* find session in existing sessions */
2481 /* connection can not be used, since it is disconnected */
2482 if ((tmp->recv_force_disconnect == GNUNET_NO) &&
2483 (tmp->send_force_disconnect == GNUNET_NO))
2485 #if DEBUG_SESSION_SELECTION
2486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2487 "Session %X selected: Using existing session to send to not-forced address\n",
2501 * Function that can be used by the transport service to transmit
2502 * a message using the plugin. Note that in the case of a
2503 * peer disconnecting, the continuation MUST be called
2504 * prior to the disconnect notification itself. This function
2505 * will be called with this peer's HELLO message to initiate
2506 * a fresh connection to another peer.
2508 * @param cls closure
2509 * @param target who should receive this message
2510 * @param msgbuf the message to transmit
2511 * @param msgbuf_size number of bytes in 'msgbuf'
2512 * @param priority how important is the message (most plugins will
2513 * ignore message priority and just FIFO)
2514 * @param to how long to wait at most for the transmission (does not
2515 * require plugins to discard the message after the timeout,
2516 * just advisory for the desired delay; most plugins will ignore
2518 * @param session which session must be used (or NULL for "any")
2519 * @param addr the address to use (can be NULL if the plugin
2520 * is "on its own" (i.e. re-use existing TCP connection))
2521 * @param addrlen length of the address in bytes
2522 * @param force_address GNUNET_YES if the plugin MUST use the given address,
2523 * GNUNET_NO means the plugin may use any other address and
2524 * GNUNET_SYSERR means that only reliable existing
2525 * bi-directional connections should be used (regardless
2527 * @param cont continuation to call once the message has
2528 * been transmitted (or if the transport is ready
2529 * for the next transmission call; or if the
2530 * peer disconnected...); can be NULL
2531 * @param cont_cls closure for cont
2532 * @return number of bytes used (on the physical network, with overheads);
2533 * -1 on hard errors (i.e. address invalid); 0 is a legal value
2534 * and does NOT mean that the message was not transmitted (DV)
2537 http_plugin_send (void *cls,
2538 const struct GNUNET_PeerIdentity *target,
2541 unsigned int priority,
2542 struct GNUNET_TIME_Relative to,
2543 struct Session *session,
2547 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
2549 struct Plugin *plugin = cls;
2550 struct HTTP_Message *msg;
2551 struct HTTP_PeerContext *pc;
2552 struct Session *ps = NULL;
2554 GNUNET_assert (cls != NULL);
2559 if (force_address == GNUNET_YES)
2560 GNUNET_asprintf (&force, "forced addr.");
2561 else if (force_address == GNUNET_NO)
2562 GNUNET_asprintf (&force, "any addr.");
2563 else if (force_address == GNUNET_SYSERR)
2564 GNUNET_asprintf (&force, "reliable bi-direc. address addr.");
2567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2568 "Transport tells me to send %u bytes to `%s' using %s (%s) and session: %X\n",
2570 GNUNET_i2s (target),
2572 http_plugin_address_to_string (NULL, addr, addrlen), session);
2573 GNUNET_free (force);
2576 pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
2580 pc = GNUNET_malloc (sizeof (struct HTTP_PeerContext));
2581 pc->plugin = plugin;
2582 pc->session_id_counter = 1;
2583 pc->last_session = NULL;
2584 memcpy (&pc->identity, target, sizeof (struct GNUNET_PeerIdentity));
2585 GNUNET_CONTAINER_multihashmap_put (plugin->peers,
2586 &pc->identity.hashPubKey,
2588 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
2589 GNUNET_STATISTICS_update (plugin->env->stats,
2590 gettext_noop ("# HTTP peers active"),
2593 ps = send_select_session (pc, addr, addrlen, force_address, session);
2594 /* session not existing, but address forced -> creating new session */
2597 if ((addr != NULL) && (addrlen != 0))
2599 ps = GNUNET_malloc (sizeof (struct Session));
2600 #if DEBUG_SESSION_SELECTION
2601 if (force_address == GNUNET_YES)
2602 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2603 "No existing connection & forced address: creating new session %X to peer %s\n",
2604 ps, GNUNET_i2s (target));
2605 if (force_address != GNUNET_YES)
2606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2607 "No existing connection: creating new session %X to peer %s\n",
2608 ps, GNUNET_i2s (target));
2610 ps->addr = GNUNET_malloc (addrlen);
2611 memcpy (ps->addr, addr, addrlen);
2612 ps->addrlen = addrlen;
2613 ps->direction = OUTBOUND;
2614 ps->recv_connected = GNUNET_NO;
2615 ps->recv_force_disconnect = GNUNET_NO;
2616 ps->send_connected = GNUNET_NO;
2617 ps->send_force_disconnect = GNUNET_NO;
2618 ps->pending_msgs_head = NULL;
2619 ps->pending_msgs_tail = NULL;
2620 ps->peercontext = pc;
2621 ps->session_id = pc->session_id_counter;
2622 ps->queue_length_cur = 0;
2623 ps->queue_length_max = GNUNET_SERVER_MAX_MESSAGE_SIZE;
2624 pc->session_id_counter++;
2625 ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
2626 if (ps->msgtok == NULL)
2627 ps->msgtok = GNUNET_SERVER_mst_create (&curl_receive_mst_cb, ps);
2628 GNUNET_CONTAINER_DLL_insert (pc->head, pc->tail, ps);
2629 GNUNET_STATISTICS_update (plugin->env->stats,
2631 ("# HTTP outbound sessions for peers active"),
2637 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2638 "No existing session found & and no address given: no way to send this message to peer `%s'!\n",
2639 GNUNET_i2s (target));
2641 return GNUNET_SYSERR;
2645 if (msgbuf_size >= (ps->queue_length_max - ps->queue_length_cur))
2647 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2648 "Queue %X full: %u bytes in queue available, message with %u is too big\n",
2649 ps, (ps->queue_length_max - ps->queue_length_cur), msgbuf_size);
2650 //return GNUNET_SYSERR;
2654 msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
2656 msg->size = msgbuf_size;
2658 msg->buf = (char *) &msg[1];
2659 msg->transmit_cont = cont;
2660 msg->transmit_cont_cls = cont_cls;
2661 memcpy (msg->buf, msgbuf, msgbuf_size);
2662 GNUNET_CONTAINER_DLL_insert (ps->pending_msgs_head,
2663 ps->pending_msgs_tail, msg);
2664 ps->queue_length_cur += msgbuf_size;
2665 if (send_check_connections (plugin, ps) == GNUNET_SYSERR)
2666 return GNUNET_SYSERR;
2667 if (force_address != GNUNET_YES)
2668 pc->last_session = ps;
2669 if (pc->last_session == NULL)
2670 pc->last_session = ps;
2676 * Function that can be used to force the plugin to disconnect
2677 * from the given peer and cancel all previous transmissions
2678 * (and their continuationc).
2680 * @param cls closure
2681 * @param target peer from which to disconnect
2684 http_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
2686 struct Plugin *plugin = cls;
2687 struct HTTP_PeerContext *pc = NULL;
2688 struct Session *ps = NULL;
2690 pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
2696 /* Telling transport that session is getting disconnected */
2697 plugin->env->session_end (plugin, target, ps);
2698 if (ps->direction == OUTBOUND)
2700 if (ps->send_endpoint != NULL)
2702 //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint));
2703 //curl_easy_cleanup(ps->send_endpoint);
2704 //ps->send_endpoint=NULL;
2705 ps->send_force_disconnect = GNUNET_YES;
2707 if (ps->recv_endpoint != NULL)
2709 //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint));
2710 //curl_easy_cleanup(ps->recv_endpoint);
2711 //ps->recv_endpoint=NULL;
2712 ps->recv_force_disconnect = GNUNET_YES;
2715 if (ps->direction == INBOUND)
2717 ps->recv_force_disconnect = GNUNET_YES;
2718 ps->send_force_disconnect = GNUNET_YES;
2720 while (ps->pending_msgs_head != NULL)
2721 remove_http_message (ps, ps->pending_msgs_head);
2722 ps->recv_active = GNUNET_NO;
2723 ps->send_active = GNUNET_NO;
2730 * Append our port and forward the result.
2732 * @param cls the 'struct PrettyPrinterContext*'
2733 * @param hostname hostname part of the address
2736 append_port (void *cls, const char *hostname)
2738 struct PrettyPrinterContext *ppc = cls;
2741 if (hostname == NULL)
2743 ppc->asc (ppc->asc_cls, NULL);
2747 GNUNET_asprintf (&ret, "%s://%s:%d", PROTOCOL_PREFIX, hostname, ppc->port);
2749 ppc->asc (ppc->asc_cls, ret);
2756 * Convert the transports address to a nice, human-readable
2759 * @param cls closure
2760 * @param type name of the transport that generated the address
2761 * @param addr one of the addresses of the host, NULL for the last address
2762 * the specific address format depends on the transport
2763 * @param addrlen length of the address
2764 * @param numeric should (IP) addresses be displayed in numeric form?
2765 * @param timeout after how long should we give up?
2766 * @param asc function to call on each string
2767 * @param asc_cls closure for asc
2770 http_plugin_address_pretty_printer (void *cls,
2775 struct GNUNET_TIME_Relative timeout,
2776 GNUNET_TRANSPORT_AddressStringCallback
2779 struct PrettyPrinterContext *ppc;
2782 struct sockaddr_in a4;
2783 struct sockaddr_in6 a6;
2784 const struct IPv4HttpAddress *t4;
2785 const struct IPv6HttpAddress *t6;
2788 if (addrlen == sizeof (struct IPv6HttpAddress))
2791 memset (&a6, 0, sizeof (a6));
2792 a6.sin6_family = AF_INET6;
2793 a6.sin6_port = t6->port;
2794 memcpy (&a6.sin6_addr, &t6->ipv6_addr, sizeof (struct in6_addr));
2795 port = ntohs (t6->port);
2799 else if (addrlen == sizeof (struct IPv4HttpAddress))
2802 memset (&a4, 0, sizeof (a4));
2803 a4.sin_family = AF_INET;
2804 a4.sin_port = t4->port;
2805 a4.sin_addr.s_addr = t4->ipv4_addr;
2806 port = ntohs (t4->ipv4_addr);
2812 /* invalid address */
2813 GNUNET_break_op (0);
2814 asc (asc_cls, NULL);
2817 ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
2819 ppc->asc_cls = asc_cls;
2821 GNUNET_RESOLVER_hostname_get (sb, sbs, !numeric, timeout, &append_port, ppc);
2827 * Another peer has suggested an address for this
2828 * peer and transport plugin. Check that this could be a valid
2829 * address. If so, consider adding it to the list
2832 * @param cls closure
2833 * @param addr pointer to the address
2834 * @param addrlen length of addr
2835 * @return GNUNET_OK if this is a plausible address for this peer
2839 http_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
2841 struct Plugin *plugin = cls;
2842 struct IPv4HttpAddress *v4;
2843 struct IPv6HttpAddress *v6;
2844 struct IPv4HttpAddressWrapper *w_tv4 = plugin->ipv4_addr_head;
2845 struct IPv6HttpAddressWrapper *w_tv6 = plugin->ipv6_addr_head;
2847 GNUNET_assert (cls != NULL);
2848 if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
2849 (addrlen != sizeof (struct IPv6HttpAddress)))
2850 return GNUNET_SYSERR;
2851 if (addrlen == sizeof (struct IPv4HttpAddress))
2853 v4 = (struct IPv4HttpAddress *) addr;
2854 if (plugin->bind4_address != NULL)
2857 memcmp (&plugin->bind4_address->sin_addr, &v4->ipv4_addr,
2861 return GNUNET_SYSERR;
2863 while (w_tv4 != NULL)
2866 memcmp (&w_tv4->addr->ipv4_addr, &v4->ipv4_addr, sizeof (uint32_t)))
2868 w_tv4 = w_tv4->next;
2873 return GNUNET_SYSERR;
2875 if (addrlen == sizeof (struct IPv6HttpAddress))
2877 v6 = (struct IPv6HttpAddress *) addr;
2878 if (plugin->bind6_address != NULL)
2881 memcmp (&plugin->bind6_address->sin6_addr, &v6->ipv6_addr,
2882 sizeof (struct in6_addr)))
2885 return GNUNET_SYSERR;
2887 while (w_tv6 != NULL)
2890 memcmp (&w_tv6->addr->ipv6_addr, &v6->ipv6_addr,
2891 sizeof (struct in6_addr)))
2893 w_tv6 = w_tv6->next;
2898 return GNUNET_SYSERR;
2900 return GNUNET_SYSERR;
2905 * Function called for a quick conversion of the binary address to
2906 * a numeric address. Note that the caller must not free the
2907 * address and that the next call to this function is allowed
2908 * to override the address again.
2910 * @param cls closure
2911 * @param addr binary address
2912 * @param addrlen length of the address
2913 * @return string representing the same address
2916 http_plugin_address_to_string (void *cls, const void *addr, size_t addrlen)
2918 const struct IPv4HttpAddress *t4;
2919 const struct IPv6HttpAddress *t6;
2920 struct sockaddr_in a4;
2921 struct sockaddr_in6 a6;
2923 static char rbuf[INET6_ADDRSTRLEN + 13];
2927 if (addrlen == sizeof (struct IPv6HttpAddress))
2929 address = GNUNET_malloc (INET6_ADDRSTRLEN);
2931 a6.sin6_addr = t6->ipv6_addr;
2932 inet_ntop (AF_INET6, &(a6.sin6_addr), address, INET6_ADDRSTRLEN);
2933 port = ntohs (t6->port);
2935 else if (addrlen == sizeof (struct IPv4HttpAddress))
2937 address = GNUNET_malloc (INET_ADDRSTRLEN);
2939 a4.sin_addr.s_addr = t4->ipv4_addr;
2940 inet_ntop (AF_INET, &(a4.sin_addr), address, INET_ADDRSTRLEN);
2941 port = ntohs (t4->port);
2945 /* invalid address */
2949 res = GNUNET_snprintf (rbuf, sizeof (rbuf), "%s:%u", address, port);
2951 GNUNET_free (address);
2952 GNUNET_assert (res != 0);
2957 * Function called by the NAT subsystem suggesting another peer wants
2958 * to connect to us via connection reversal. Try to connect back to the
2961 * @param cls closure
2962 * @param addr address to try
2963 * @param addrlen number of bytes in addr
2966 try_connection_reversal (void *cls,
2967 const struct sockaddr *addr, socklen_t addrlen)
2973 tcp_nat_cb_add_addr (void *cls,
2975 const struct sockaddr *addr, socklen_t addrlen)
2977 struct Plugin *plugin = cls;
2978 struct IPv4HttpAddress *t4 = NULL;
2979 struct IPv4HttpAddressWrapper *w_t4 = NULL;
2980 struct IPv6HttpAddress *t6 = NULL;
2981 struct IPv6HttpAddressWrapper *w_t6 = NULL;
2984 af = addr->sa_family;
2988 w_t4 = plugin->ipv4_addr_head;
2989 while (w_t4 != NULL)
2991 int res = memcmp (&w_t4->addr->ipv4_addr,
2992 &((struct sockaddr_in *) addr)->sin_addr,
2993 sizeof (struct in_addr));
3001 w_t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddressWrapper));
3002 t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddress));
3003 memcpy (&t4->ipv4_addr,
3004 &((struct sockaddr_in *) addr)->sin_addr,
3005 sizeof (struct in_addr));
3006 t4->port = htons (plugin->port_inbound);
3010 GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head,
3011 plugin->ipv4_addr_tail, w_t4);
3013 plugin->env->notify_address (plugin->env->cls,
3015 w_t4->addr, sizeof (struct IPv4HttpAddress));
3019 w_t6 = plugin->ipv6_addr_head;
3022 int res = memcmp (&w_t6->addr->ipv6_addr,
3023 &((struct sockaddr_in6 *) addr)->sin6_addr,
3024 sizeof (struct in6_addr));
3032 w_t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddressWrapper));
3033 t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddress));
3035 memcpy (&t6->ipv6_addr,
3036 &((struct sockaddr_in6 *) addr)->sin6_addr,
3037 sizeof (struct in6_addr));
3038 t6->port = htons (plugin->port_inbound);
3042 GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head,
3043 plugin->ipv6_addr_tail, w_t6);
3045 plugin->env->notify_address (plugin->env->cls,
3047 w_t6->addr, sizeof (struct IPv6HttpAddress));
3056 tcp_nat_cb_remove_addr (void *cls,
3058 const struct sockaddr *addr, socklen_t addrlen)
3060 struct Plugin *plugin = cls;
3061 struct IPv4HttpAddressWrapper *w_t4 = NULL;
3062 struct IPv6HttpAddressWrapper *w_t6 = NULL;
3065 af = addr->sa_family;
3069 w_t4 = plugin->ipv4_addr_head;
3070 while (w_t4 != NULL)
3072 int res = memcmp (&w_t4->addr->ipv4_addr,
3073 &((struct sockaddr_in *) addr)->sin_addr,
3074 sizeof (struct in_addr));
3082 plugin->env->notify_address (plugin->env->cls,
3084 w_t4->addr, sizeof (struct IPv4HttpAddress));
3086 GNUNET_CONTAINER_DLL_remove (plugin->ipv4_addr_head,
3087 plugin->ipv4_addr_tail, w_t4);
3088 GNUNET_free (w_t4->addr);
3092 w_t6 = plugin->ipv6_addr_head;
3093 while (w_t6 != NULL)
3095 int res = memcmp (&w_t6->addr->ipv6_addr,
3096 &((struct sockaddr_in6 *) addr)->sin6_addr,
3097 sizeof (struct in6_addr));
3105 plugin->env->notify_address (plugin->env->cls,
3107 w_t6->addr, sizeof (struct IPv6HttpAddress));
3109 GNUNET_CONTAINER_DLL_remove (plugin->ipv6_addr_head,
3110 plugin->ipv6_addr_tail, w_t6);
3111 GNUNET_free (w_t6->addr);
3121 * Our external IP address/port mapping has changed.
3123 * @param cls closure, the 'struct LocalAddrList'
3124 * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
3125 * the previous (now invalid) one
3126 * @param addr either the previous or the new public IP address
3127 * @param addrlen actual lenght of the address
3130 tcp_nat_port_map_callback (void *cls,
3132 const struct sockaddr *addr, socklen_t addrlen)
3134 GNUNET_assert (cls != NULL);
3136 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
3137 "NPMC called to %s address `%s'\n",
3138 (add_remove == GNUNET_YES) ? "remove" : "add",
3139 GNUNET_a2s (addr, addrlen));
3141 /* convert 'addr' to our internal format */
3145 tcp_nat_cb_add_addr (cls, add_remove, addr, addrlen);
3148 tcp_nat_cb_remove_addr (cls, add_remove, addr, addrlen);
3154 * Exit point from the plugin.
3157 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
3159 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
3160 struct Plugin *plugin = api->cls;
3162 struct IPv4HttpAddressWrapper *w_t4;
3163 struct IPv6HttpAddressWrapper *w_t6;
3165 GNUNET_assert (cls != NULL);
3167 if (plugin->nat != NULL)
3168 GNUNET_NAT_unregister (plugin->nat);
3170 if (plugin->http_server_daemon_v4 != NULL)
3172 MHD_stop_daemon (plugin->http_server_daemon_v4);
3173 plugin->http_server_daemon_v4 = NULL;
3175 if (plugin->http_server_daemon_v6 != NULL)
3177 MHD_stop_daemon (plugin->http_server_daemon_v6);
3178 plugin->http_server_daemon_v6 = NULL;
3180 if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
3182 GNUNET_SCHEDULER_cancel (plugin->http_server_task_v4);
3183 plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
3185 if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
3187 GNUNET_SCHEDULER_cancel (plugin->http_server_task_v6);
3188 plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
3191 while (plugin->ipv4_addr_head != NULL)
3193 w_t4 = plugin->ipv4_addr_head;
3194 GNUNET_CONTAINER_DLL_remove (plugin->ipv4_addr_head, plugin->ipv4_addr_tail,
3196 GNUNET_free (w_t4->addr);
3200 while (plugin->ipv6_addr_head != NULL)
3202 w_t6 = plugin->ipv6_addr_head;
3203 GNUNET_CONTAINER_DLL_remove (plugin->ipv6_addr_head, plugin->ipv6_addr_tail,
3205 GNUNET_free (w_t6->addr);
3209 /* free all peer information */
3210 if (plugin->peers != NULL)
3212 GNUNET_CONTAINER_multihashmap_iterate (plugin->peers,
3213 &remove_peer_context_Iterator,
3215 GNUNET_CONTAINER_multihashmap_destroy (plugin->peers);
3217 if (plugin->multi_handle != NULL)
3219 mret = curl_multi_cleanup (plugin->multi_handle);
3220 if (CURLM_OK != mret)
3221 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3222 "curl multihandle clean up failed\n");
3223 plugin->multi_handle = NULL;
3225 curl_global_cleanup ();
3227 if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
3229 GNUNET_SCHEDULER_cancel (plugin->http_curl_task);
3230 plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
3234 GNUNET_free_non_null (plugin->bind4_address);
3235 GNUNET_free_non_null (plugin->bind6_address);
3236 GNUNET_free_non_null (plugin->bind_hostname);
3238 GNUNET_free_non_null (plugin->crypto_init);
3239 GNUNET_free_non_null (plugin->cert);
3240 GNUNET_free_non_null (plugin->key);
3242 GNUNET_free (plugin);
3245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3246 "Unload %s plugin complete...\n", PROTOCOL_PREFIX);
3253 load_certificate (const char *file)
3255 struct GNUNET_DISK_FileHandle *gn_file;
3259 if (0 != STAT (file, &fstat))
3261 text = GNUNET_malloc (fstat.st_size + 1);
3263 GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
3264 GNUNET_DISK_PERM_USER_READ);
3265 if (gn_file == NULL)
3270 if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fstat.st_size))
3273 GNUNET_DISK_file_close (gn_file);
3276 text[fstat.st_size] = '\0';
3277 GNUNET_DISK_file_close (gn_file);
3284 * Entry point for the plugin.
3287 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
3289 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
3290 struct Plugin *plugin;
3291 struct GNUNET_TRANSPORT_PluginFunctions *api;
3292 struct GNUNET_TIME_Relative gn_timeout;
3293 long long unsigned int port;
3294 unsigned long long tneigh;
3295 struct sockaddr **addrs;
3296 socklen_t *addrlens;
3298 char *component_name;
3301 char *key_file = NULL;
3302 char *cert_file = NULL;
3305 GNUNET_assert (cls != NULL);
3307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3308 "Starting %s plugin...\n", PROTOCOL_PREFIX);
3310 GNUNET_asprintf (&component_name, "transport-%s", PROTOCOL_PREFIX);
3312 plugin = GNUNET_malloc (sizeof (struct Plugin));
3313 plugin->stats = env->stats;
3315 plugin->peers = NULL;
3316 plugin->bind4_address = NULL;
3317 plugin->bind6_address = NULL;
3318 plugin->use_ipv6 = GNUNET_YES;
3319 plugin->use_ipv4 = GNUNET_YES;
3320 plugin->use_localaddresses = GNUNET_NO;
3322 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
3324 api->send = &http_plugin_send;
3325 api->disconnect = &http_plugin_disconnect;
3326 api->address_pretty_printer = &http_plugin_address_pretty_printer;
3327 api->check_address = &http_plugin_address_suggested;
3328 api->address_to_string = &http_plugin_address_to_string;
3330 /* Hashing our identity to use it in URLs */
3331 GNUNET_CRYPTO_hash_to_enc (&(plugin->env->my_identity->hashPubKey),
3332 &plugin->my_ascii_hash_ident);
3336 GNUNET_CONFIGURATION_get_value_number (env->cfg,
3338 "MAX_CONNECTIONS", &tneigh))
3340 plugin->max_connect_per_transport = tneigh;
3344 if (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "USE_IPv6"))
3346 plugin->use_ipv6 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
3351 if (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "USE_IPv4"))
3353 plugin->use_ipv4 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
3357 /* use local addresses? */
3359 if (GNUNET_CONFIGURATION_have_value (env->cfg,
3360 component_name, "USE_LOCALADDR"))
3362 plugin->use_localaddresses = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
3366 /* Reading port number from config file */
3368 GNUNET_CONFIGURATION_get_value_number (env->cfg,
3370 "PORT", &port)) || (port > 65535))
3372 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
3375 ("Require valid port number for transport plugin `%s' in configuration!\n"),
3377 GNUNET_free (component_name);
3378 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3382 /* Reading ipv4 addresse to bind to from config file */
3383 if ((plugin->use_ipv4 == GNUNET_YES) &&
3384 (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "BINDTO4")))
3386 GNUNET_break (GNUNET_OK ==
3387 GNUNET_CONFIGURATION_get_value_string (env->cfg,
3390 &plugin->bind_hostname));
3391 plugin->bind4_address = GNUNET_malloc (sizeof (struct sockaddr_in));
3392 plugin->bind4_address->sin_family = AF_INET;
3393 plugin->bind4_address->sin_port = htons (port);
3395 if (plugin->bind_hostname != NULL)
3398 (AF_INET, plugin->bind_hostname,
3399 &plugin->bind4_address->sin_addr) <= 0)
3401 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
3404 ("Misconfigured address to bind to in configuration!\n"));
3405 GNUNET_free (plugin->bind4_address);
3406 GNUNET_free (plugin->bind_hostname);
3407 plugin->bind_hostname = NULL;
3408 plugin->bind4_address = NULL;
3413 /* Reading ipv4 addresse to bind to from config file */
3414 if ((plugin->use_ipv6 == GNUNET_YES) &&
3415 (GNUNET_CONFIGURATION_have_value (env->cfg, component_name, "BINDTO6")))
3417 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (env->cfg,
3420 &plugin->bind_hostname))
3422 plugin->bind6_address = GNUNET_malloc (sizeof (struct sockaddr_in6));
3423 plugin->bind6_address->sin6_family = AF_INET6;
3424 plugin->bind6_address->sin6_port = htons (port);
3425 if (plugin->bind_hostname != NULL)
3428 (AF_INET6, plugin->bind_hostname,
3429 &plugin->bind6_address->sin6_addr) <= 0)
3431 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
3434 ("Misconfigured address to bind to in configuration!\n"));
3435 GNUNET_free (plugin->bind6_address);
3436 GNUNET_free (plugin->bind_hostname);
3437 plugin->bind_hostname = NULL;
3438 plugin->bind6_address = NULL;
3445 /* Reading HTTPS crypto related configuration */
3446 /* Get crypto init string from config */
3448 GNUNET_CONFIGURATION_get_value_string (env->cfg,
3451 &plugin->crypto_init)) ||
3453 GNUNET_CONFIGURATION_get_value_filename (env->cfg,
3458 GNUNET_CONFIGURATION_get_value_filename (env->cfg,
3460 "CERT_FILE", &cert_file)))
3462 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
3465 ("Required configuration options missing in section `%s'\n"),
3467 GNUNET_free (component_name);
3468 GNUNET_free_non_null (key_file);
3469 GNUNET_free_non_null (cert_file);
3470 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3474 /* read key & certificates from file */
3475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3476 "Loading TLS certificate `%s' `%s'\n", key_file, cert_file);
3478 plugin->key = load_certificate (key_file);
3479 plugin->cert = load_certificate (cert_file);
3481 if ((plugin->key == NULL) || (plugin->cert == NULL))
3483 struct GNUNET_OS_Process *certcreation;
3485 GNUNET_free_non_null (plugin->key);
3487 GNUNET_free_non_null (plugin->cert);
3488 plugin->cert = NULL;
3490 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3491 "No usable TLS certificate found, creating certificate\n");
3494 certcreation = GNUNET_OS_start_process (NULL, NULL,
3495 "gnunet-transport-certificate-creation",
3496 "gnunet-transport-certificate-creation",
3497 key_file, cert_file, NULL);
3498 if (certcreation == NULL)
3500 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
3503 ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
3504 GNUNET_free (key_file);
3505 GNUNET_free (cert_file);
3506 GNUNET_free (component_name);
3507 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3510 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (certcreation));
3511 GNUNET_OS_process_close (certcreation);
3512 plugin->key = load_certificate (key_file);
3513 plugin->cert = load_certificate (cert_file);
3515 if ((plugin->key == NULL) || (plugin->cert == NULL))
3517 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
3520 ("No usable TLS certificate found and creating one failed!\n"),
3522 GNUNET_free (key_file);
3523 GNUNET_free (cert_file);
3524 GNUNET_free (component_name);
3525 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3528 GNUNET_free (key_file);
3529 GNUNET_free (cert_file);
3531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
3535 GNUNET_assert ((port > 0) && (port <= 65535));
3536 plugin->port_inbound = port;
3537 gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
3538 unsigned int timeout = (gn_timeout.rel_value) / 1000;
3540 if ((plugin->http_server_daemon_v6 == NULL) &&
3541 (plugin->use_ipv6 == GNUNET_YES) && (port != 0))
3543 struct sockaddr *tmp = (struct sockaddr *) plugin->bind6_address;
3545 plugin->http_server_daemon_v6 = MHD_start_daemon (
3554 &mhd_accept_cb, plugin,
3555 &mhd_access_cb, plugin,
3556 MHD_OPTION_SOCK_ADDR,
3558 MHD_OPTION_CONNECTION_LIMIT,
3560 plugin->max_connect_per_transport,
3562 MHD_OPTION_HTTPS_PRIORITIES,
3563 plugin->crypto_init,
3564 MHD_OPTION_HTTPS_MEM_KEY,
3566 MHD_OPTION_HTTPS_MEM_CERT,
3569 MHD_OPTION_CONNECTION_TIMEOUT,
3570 (unsigned int) timeout,
3571 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
3573 GNUNET_SERVER_MAX_MESSAGE_SIZE),
3574 MHD_OPTION_NOTIFY_COMPLETED,
3575 &mhd_termination_cb,
3577 MHD_OPTION_EXTERNAL_LOGGER,
3582 if ((plugin->http_server_daemon_v4 == NULL) &&
3583 (plugin->use_ipv4 == GNUNET_YES) && (port != 0))
3585 plugin->http_server_daemon_v4 = MHD_start_daemon (
3594 &mhd_accept_cb, plugin,
3595 &mhd_access_cb, plugin,
3596 MHD_OPTION_SOCK_ADDR,
3597 (struct sockaddr_in *)
3598 plugin->bind4_address,
3599 MHD_OPTION_CONNECTION_LIMIT,
3601 plugin->max_connect_per_transport,
3603 MHD_OPTION_HTTPS_PRIORITIES,
3604 plugin->crypto_init,
3605 MHD_OPTION_HTTPS_MEM_KEY,
3607 MHD_OPTION_HTTPS_MEM_CERT,
3610 MHD_OPTION_CONNECTION_TIMEOUT,
3611 (unsigned int) timeout,
3612 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
3614 GNUNET_SERVER_MAX_MESSAGE_SIZE),
3615 MHD_OPTION_NOTIFY_COMPLETED,
3616 &mhd_termination_cb,
3618 MHD_OPTION_EXTERNAL_LOGGER,
3623 if (plugin->http_server_daemon_v4 != NULL)
3624 plugin->http_server_task_v4 =
3625 http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
3626 if (plugin->http_server_daemon_v6 != NULL)
3627 plugin->http_server_task_v6 =
3628 http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
3631 if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
3634 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3635 "Starting MHD with IPv4 bound to %s with port %u\n",
3636 (plugin->bind_hostname !=
3637 NULL) ? plugin->bind_hostname : "every address", port);
3640 else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) &&
3641 (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK))
3644 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3645 "Starting MHD with IPv6 bound to %s with port %u\n",
3646 (plugin->bind_hostname !=
3647 NULL) ? plugin->bind_hostname : "every address", port);
3650 else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) &&
3651 (plugin->http_server_task_v4 == GNUNET_SCHEDULER_NO_TASK))
3654 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3655 "Starting MHD with IPv4 and IPv6 bound to %s with port %u\n",
3656 (plugin->bind_hostname !=
3657 NULL) ? plugin->bind_hostname : "every address", port);
3664 if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_YES))
3665 GNUNET_asprintf (&tmp, "with IPv4 and IPv6 enabled");
3666 if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_YES))
3667 GNUNET_asprintf (&tmp, "with IPv4 enabled");
3668 if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_NO))
3669 GNUNET_asprintf (&tmp, "with IPv6 enabled");
3670 if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_NO))
3671 GNUNET_asprintf (&tmp, "with NO IP PROTOCOL enabled");
3672 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3674 ("HTTP Server with %s could not be started on port %u! %s plugin failed!\n"),
3675 tmp, port, PROTOCOL_PREFIX);
3677 GNUNET_free (component_name);
3678 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3682 /* Initializing cURL */
3683 curl_global_init (CURL_GLOBAL_ALL);
3684 plugin->multi_handle = curl_multi_init ();
3686 if (NULL == plugin->multi_handle)
3688 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
3691 ("Could not initialize curl multi handle, failed to start %s plugin!\n"),
3693 GNUNET_free (component_name);
3694 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
3698 ret = GNUNET_SERVICE_get_server_addresses (component_name,
3699 env->cfg, &addrs, &addrlens);
3701 if (ret != GNUNET_SYSERR)
3703 plugin->nat = GNUNET_NAT_register (env->cfg,
3707 (const struct sockaddr **) addrs,
3709 &tcp_nat_port_map_callback,
3710 &try_connection_reversal, plugin);
3714 GNUNET_assert (addrs[ret] != NULL);
3715 GNUNET_free (addrs[ret]);
3717 GNUNET_free_non_null (addrs);
3718 GNUNET_free_non_null (addrlens);
3722 plugin->nat = GNUNET_NAT_register (env->cfg,
3726 NULL, &try_connection_reversal, plugin);
3729 plugin->peers = GNUNET_CONTAINER_multihashmap_create (10);
3731 GNUNET_free (component_name);
3732 //GNUNET_SCHEDULER_add_now(address_notification, plugin);
3736 /* end of plugin_transport_http.c */