X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Ftransport%2Fplugin_transport_http.c;h=c2f8a6091b1c32c65064007762981c72c701b2cb;hb=dd253b7f7591f0869f8ea14ee111b7d3b3e480b6;hp=2a4f92141c3dfb7a67224762355dd94c8e4bc2f4;hpb=78a7e3894d02b78616e8d33d861e74b321ab5582;p=oweals%2Fgnunet.git diff --git a/src/transport/plugin_transport_http.c b/src/transport/plugin_transport_http.c index 2a4f92141..c2f8a6091 100644 --- a/src/transport/plugin_transport_http.c +++ b/src/transport/plugin_transport_http.c @@ -25,6 +25,7 @@ */ #include "platform.h" +#include "gnunet_common.h" #include "gnunet_constants.h" #include "gnunet_protocols.h" #include "gnunet_connection_lib.h" @@ -39,13 +40,31 @@ #include "microhttpd.h" #include +#if BUILD_HTTPS +#define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_init +#define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_done +#define LIBGNUNET_PLUGIN_TRANSPORT_COMPONENT transport_https +#define PROTOCOL_PREFIX "https" +#else +#define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_init +#define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_done +#define LIBGNUNET_PLUGIN_TRANSPORT_COMPONENT transport_http +#define PROTOCOL_PREFIX "http" +#endif -#define DEBUG_CURL GNUNET_NO #define DEBUG_HTTP GNUNET_NO +#define DEBUG_CURL GNUNET_NO +#define DEBUG_MHD GNUNET_NO +#define DEBUG_CONNECTIONS GNUNET_NO +#define DEBUG_SESSION_SELECTION GNUNET_NO + +#define CURL_TCP_NODELAY GNUNET_YES #define INBOUND GNUNET_NO #define OUTBOUND GNUNET_YES + + /** * Text of the response sent back after the last bytes of a PUT * request have been received (just to formally obey the HTTP @@ -70,6 +89,7 @@ */ #define HTTP_CONNECT_TIMEOUT 30 + /** * Network format for IPv4 addresses. */ @@ -152,6 +172,9 @@ struct HTTP_Message struct HTTP_PeerContext { + /** + * peer's identity + */ struct GNUNET_PeerIdentity identity; /** @@ -159,8 +182,27 @@ struct HTTP_PeerContext */ struct Plugin *plugin; + /** + * Linked list of connections with this peer + * head + */ struct Session * head; + + /** + * Linked list of connections with this peer + * tail + */ struct Session * tail; + + /** + * id for next session + */ + size_t session_id_counter; + + /** + * Last session used to send data + */ + struct Session * last_session; }; @@ -235,6 +277,11 @@ struct Session */ unsigned int send_active; + /** + * connection disconnect forced (e.g. from transport) + */ + unsigned int send_force_disconnect; + /** * is session connected to receive data? */ @@ -245,15 +292,28 @@ struct Session */ unsigned int recv_active; + /** + * connection disconnect forced (e.g. from transport) + */ + unsigned int recv_force_disconnect; + + /** + * id for next session + * NOTE: 0 is not an ID, zero is not defined. A correct ID is always > 0 + */ + size_t session_id; + /** * entity managing sending data - * outbound session: pointer to curl easy handle + * outbound session: CURL * + * inbound session: mhd_connection * */ void * send_endpoint; /** * entity managing recieving data - * outbound session: pointer to curl easy handle + * outbound session: CURL * + * inbound session: mhd_connection * */ void * recv_endpoint; }; @@ -268,6 +328,14 @@ struct Plugin */ struct GNUNET_TRANSPORT_PluginEnvironment *env; + /** + * Handle for reporting statistics. + */ + struct GNUNET_STATISTICS_Handle *stats; + + /** + * Plugin Port + */ unsigned int port_inbound; struct GNUNET_CONTAINER_MultiHashMap *peers; @@ -295,7 +363,7 @@ struct Plugin /** * The task sending data */ - GNUNET_SCHEDULER_TaskIdentifier http_server_task_send; + GNUNET_SCHEDULER_TaskIdentifier http_curl_task; /** * cURL Multihandle @@ -307,6 +375,48 @@ struct Plugin * This string is used to distinguish between connections and is added to the urls */ struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident; + + /** + * IPv4 Address the plugin binds to + */ + struct sockaddr_in * bind4_address; + + /** + * IPv6 Address the plugins binds to + */ + struct sockaddr_in6 * bind6_address; + + /** + * Hostname to bind to + */ + char * bind_hostname; + + /** + * Is IPv4 enabled? + */ + int use_ipv6; + + /** + * Is IPv6 enabled? + */ + int use_ipv4; + + /** + * Closure passed by MHD to the mhd_logger function + */ + void * mhd_log; + + /* only needed for HTTPS plugin */ +#if BUILD_HTTPS + /* The certificate MHD uses as an \0 terminated string */ + char * cert; + + /* The private key MHD uses as an \0 terminated string */ + char * key; + + /* crypto init string */ + char * crypto_init; +#endif }; @@ -326,124 +436,377 @@ http_plugin_address_to_string (void *cls, const void *addr, size_t addrlen); -static char * create_url(void * cls, const void * addr, size_t addrlen) + +/** + * Call MHD to process pending ipv4 requests and then go back + * and schedule the next run. + */ +static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +/** + * Call MHD to process pending ipv6 requests and then go back + * and schedule the next run. + */ +static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + +/** + * Function setting up curl handle and selecting message to send + * @param plugin plugin + * @param ses session to send data to + * @param con connection + * @return bytes sent to peer + */ +static int send_check_connections (struct Plugin *plugin, struct Session *ps); + +/** + * Function setting up file descriptors and scheduling task to run + * @param cls closure + * @param ses session to send data to + * @param + */ +static int curl_schedule (struct Plugin *plugin); + + +/** + * Creates a valid url from passed address and id + * @param plugin plugin + * @param addr address to create url from + * @param addrlen address lenth + * @param id session id + * @return the created url + */ +static char * create_url(struct Plugin *plugin, const void * addr, size_t addrlen, size_t id) { - struct Plugin *plugin = cls; char *url = NULL; + char *addr_str = (char *) http_plugin_address_to_string(NULL, addr, addrlen); GNUNET_assert ((addr!=NULL) && (addrlen != 0)); GNUNET_asprintf(&url, - "http://%s/%s", - http_plugin_address_to_string(NULL, addr, addrlen), - (char *) (&plugin->my_ascii_hash_ident)); - + "%s://%s/%s;%u", PROTOCOL_PREFIX, addr_str, + (char *) (&plugin->my_ascii_hash_ident),id); + GNUNET_free_non_null(addr_str); return url; } /** * Removes a message from the linked list of messages - * @param con connection to remove message from - * @param msg message to remove + * @param ps session + * @param msg message * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success */ - -static int remove_http_message(struct Session * ps, struct HTTP_Message * msg) +static int remove_http_message (struct Session * ps, struct HTTP_Message * msg) { GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_tail,msg); GNUNET_free(msg); return GNUNET_OK; } -static struct Session * get_Session (void * cls, struct HTTP_PeerContext *pc, const void * addr, size_t addr_len) +/** + * Iterator to remove peer context + * @param cls the plugin + * @key the peers public key hashcode + * @value the peer context + * @return GNUNET_YES on success + */ +int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value) { - struct Session * cc = pc->head; - struct Session * con = NULL; - unsigned int count = 0; + struct Plugin *plugin = cls; + struct HTTP_PeerContext * pc = value; + struct Session * ps = pc->head; + struct Session * tmp = NULL; + struct HTTP_Message * msg = NULL; + struct HTTP_Message * msg_tmp = NULL; +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing context for peer `%s'\n",GNUNET_i2s(&pc->identity)); +#endif + GNUNET_CONTAINER_multihashmap_remove (plugin->peers, &pc->identity.hashPubKey, pc); + while (ps!=NULL) + { + plugin->env->session_end(plugin, &pc->identity, ps); + tmp = ps->next; + + GNUNET_free_non_null (ps->addr); + GNUNET_free(ps->url); + if (ps->msgtok != NULL) + GNUNET_SERVER_mst_destroy (ps->msgtok); - GNUNET_assert((addr_len == sizeof (struct IPv4HttpAddress)) || (addr_len == sizeof (struct IPv6HttpAddress))); - while (cc!=NULL) + msg = ps->pending_msgs_head; + while (msg!=NULL) + { + msg_tmp = msg->next; + GNUNET_free(msg); + msg = msg_tmp; + } + if (ps->direction==OUTBOUND) + { + if (ps->send_endpoint!=NULL) + curl_easy_cleanup(ps->send_endpoint); + if (ps->recv_endpoint!=NULL) + curl_easy_cleanup(ps->recv_endpoint); + } + + GNUNET_free(ps); + ps=tmp; + } + GNUNET_free(pc); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP peers active"), + -1, + GNUNET_NO); + return GNUNET_YES; +} + + +/** + * Removes a session from the linked list of sessions + * @param pc peer context + * @param ps session + * @param call_msg_cont GNUNET_YES to call pending message continuations, otherwise no + * @param call_msg_cont_result result to call message continuations with + * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success + */ +static int remove_session (struct HTTP_PeerContext * pc, struct Session * ps, int call_msg_cont, int call_msg_cont_result) +{ + struct HTTP_Message * msg; + struct Plugin * plugin = ps->peercontext->plugin; + +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: removing %s session %X with id %u\n", ps, (ps->direction == INBOUND) ? "inbound" : "outbound", ps, ps->session_id); +#endif + plugin->env->session_end(plugin, &pc->identity, ps); + + GNUNET_free_non_null (ps->addr); + GNUNET_SERVER_mst_destroy (ps->msgtok); + GNUNET_free(ps->url); + + if (ps->direction==INBOUND) { - if (addr_len == cc->addrlen) + if (ps->recv_endpoint != NULL) + { + curl_easy_cleanup(ps->recv_endpoint); + ps->recv_endpoint = NULL; + } + if (ps->send_endpoint != NULL) + { + curl_easy_cleanup(ps->send_endpoint); + ps->send_endpoint = NULL; + } + } + + msg = ps->pending_msgs_head; + while (msg!=NULL) + { + if ((call_msg_cont == GNUNET_YES) && (msg->transmit_cont!=NULL)) { - if (0 == memcmp(cc->addr, addr, addr_len)) + msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,call_msg_cont_result); + } + GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_head,msg); + GNUNET_free(msg); + msg = ps->pending_msgs_head; + } + + GNUNET_CONTAINER_DLL_remove(pc->head,pc->tail,ps); + GNUNET_free(ps); + ps = NULL; + + /* no sessions left remove peer */ + if (pc->head==NULL) + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No sessions left for peer `%s', removing context\n",GNUNET_i2s(&pc->identity)); +#endif + remove_peer_context_Iterator(plugin, &pc->identity.hashPubKey, pc); + } + + return GNUNET_OK; +} + + +/** + * Add the IP of our network interface to the list of + * our external IP addresses. + * + * @param cls the 'struct Plugin*' + * @param name name of the interface + * @param isDefault do we think this may be our default interface + * @param addr address of the interface + * @param addrlen number of bytes in addr + * @return GNUNET_OK to continue iterating + */ +static int +process_interfaces (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, socklen_t addrlen) +{ + struct Plugin *plugin = cls; + struct IPv4HttpAddress * t4; + struct IPv6HttpAddress * t6; + int af; + + + GNUNET_assert(cls !=NULL); + af = addr->sa_family; + if ((af == AF_INET) && (plugin->use_ipv4 == GNUNET_YES) && (plugin->bind6_address == NULL)) + { + struct in_addr bnd_cmp = ((struct sockaddr_in *) addr)->sin_addr; + t4 = GNUNET_malloc(sizeof(struct IPv4HttpAddress)); + /* Not skipping loopback addresses + if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr)) { - con = cc; - break; + + return GNUNET_OK; + } + */ + t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; + t4->u_port = htons (plugin->port_inbound); + if (plugin->bind4_address != NULL) + { + if (0 == memcmp(&plugin->bind4_address->sin_addr, &bnd_cmp, sizeof (struct in_addr))) + { + plugin->env->notify_address(plugin->env->cls,PROTOCOL_PREFIX,t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL); + } } + else + { + plugin->env->notify_address(plugin->env->cls,PROTOCOL_PREFIX,t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL); + } + GNUNET_free (t4); } - count++; - cc=cc->next; - } - return con; + else if ((af == AF_INET6) && (plugin->use_ipv6 == GNUNET_YES) && (plugin->bind4_address == NULL)) + { + struct in6_addr bnd_cmp6 = ((struct sockaddr_in6 *) addr)->sin6_addr; + if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr)) + { + return GNUNET_OK; + } + t6 = GNUNET_malloc(sizeof(struct IPv6HttpAddress)); + GNUNET_assert(t6 != NULL); + if (plugin->bind6_address != NULL) + { + if (0 == memcmp(&plugin->bind6_address->sin6_addr, &bnd_cmp6, sizeof (struct in6_addr))) + { + memcpy (&t6->ipv6_addr, + &((struct sockaddr_in6 *) addr)->sin6_addr, + sizeof (struct in6_addr)); + t6->u6_port = htons (plugin->port_inbound); + plugin->env->notify_address(plugin->env->cls,PROTOCOL_PREFIX,t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL); + } + } + else + { + memcpy (&t6->ipv6_addr, + &((struct sockaddr_in6 *) addr)->sin6_addr, + sizeof (struct in6_addr)); + t6->u6_port = htons (plugin->port_inbound); + plugin->env->notify_address(plugin->env->cls,PROTOCOL_PREFIX,t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL); + } + GNUNET_free (t6); + } + return GNUNET_OK; } +/** + * External logging function for MHD + * @param arg arguments + * @param fmt format string + * @param ap list of arguments + */ +void mhd_logger (void * arg, const char * fmt, va_list ap) +{ + char text[1024]; + vsnprintf(text, 1024, fmt, ap); + va_end(ap); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR,"MHD: %s \n", text); +} + /** * Callback called by MHD when a connection is terminated + * @param cls closure + * @param connection the terminated connection + * @httpSessionCache the mhd session reference */ -static void requestCompletedCallback (void *cls, struct MHD_Connection * connection, void **httpSessionCache) +static void mhd_termination_cb (void *cls, struct MHD_Connection * connection, void **httpSessionCache) { struct Session * ps = *httpSessionCache; if (ps == NULL) return; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection from peer `%s' was terminated\n",GNUNET_i2s(&ps->peercontext->identity)); - /* session set to inactive */ - //ps-> = GNUNET_NO; - //con->is_bad_request = GNUNET_NO; + struct HTTP_PeerContext * pc = ps->peercontext; + + if (connection==ps->recv_endpoint) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity)); +#endif + ps->recv_active = GNUNET_NO; + ps->recv_connected = GNUNET_NO; + ps->recv_endpoint = NULL; + } + if (connection==ps->send_endpoint) + { + + ps->send_active = GNUNET_NO; + ps->send_connected = GNUNET_NO; + ps->send_endpoint = NULL; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity)); +#endif + } + + /* if both connections disconnected, remove session */ + if ((ps->send_connected == GNUNET_NO) && (ps->recv_connected == GNUNET_NO)) + { + GNUNET_STATISTICS_update (pc->plugin->env->stats, + gettext_noop ("# HTTP inbound sessions for peers active"), + -1, + GNUNET_NO); + remove_session(pc,ps,GNUNET_YES,GNUNET_SYSERR); + } } +/** + * Callback called by MessageStreamTokenizer when a message has arrived + * @param cls current session as closure + * @param client clien + * @param message the message to be forwarded to transport service + */ + static void mhd_write_mst_cb (void *cls, void *client, const struct GNUNET_MessageHeader *message) { struct Session *ps = cls; - struct HTTP_PeerContext *pc = ps->peercontext; GNUNET_assert(ps != NULL); - GNUNET_assert(pc != NULL); + struct HTTP_PeerContext *pc = ps->peercontext; + GNUNET_assert(pc != NULL); +#if DEBUG_HTTP GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n", + "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n", + ps, ntohs(message->type), ntohs(message->size), GNUNET_i2s(&(ps->peercontext)->identity),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen)); - +#endif pc->plugin->env->receive (ps->peercontext->plugin->env->cls, &pc->identity, message, 1, ps, - ps->addr, - ps->addrlen); + NULL, + 0); } -static void curl_write_mst_cb (void *cls, - void *client, - const struct GNUNET_MessageHeader *message) -{ - struct Session *ps = cls; - struct HTTP_PeerContext *pc = ps->peercontext; - GNUNET_assert(ps != NULL); - GNUNET_assert(pc != NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n", - ntohs(message->type), - ntohs(message->size), - GNUNET_i2s(&(pc->identity)),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen)); - - pc->plugin->env->receive (pc->plugin->env->cls, - &pc->identity, - message, 1, ps, - ps->addr, - ps->addrlen); -} - - /** - * Check if ip is allowed to connect. + * Check if incoming connection is accepted. + * NOTE: Here every connection is accepted + * @param cls plugin as closure + * @param addr address of incoming connection + * @param addr_len address length of incoming connection + * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected + * */ static int -acceptPolicyCallback (void *cls, - const struct sockaddr *addr, socklen_t addr_len) +mhd_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len) { #if 0 struct Plugin *plugin = cls; @@ -452,38 +815,55 @@ acceptPolicyCallback (void *cls, return MHD_YES; } -int server_read_callback (void *cls, uint64_t pos, char *buf, int max) -{ - int bytes_read = 0; +/** + * Callback called by MHD when it needs data to send + * @param cls current session + * @param pos position in buffer + * @param buf the buffer to write data to + * @param max max number of bytes available in buffer + * @return bytes written to buffer + */ +int mhd_send_callback (void *cls, uint64_t pos, char *buf, int max) +{ struct Session * ps = cls; struct HTTP_PeerContext * pc; struct HTTP_Message * msg; - int res;res=5; + int bytes_read = 0; GNUNET_assert (ps!=NULL); + pc = ps->peercontext; msg = ps->pending_msgs_tail; + if (ps->send_force_disconnect==GNUNET_YES) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound forced to disconnect\n",ps); +#endif + return -1; + } if (msg!=NULL) { if ((msg->size-msg->pos) <= max) - { - memcpy(buf,&msg->buf[pos],(msg->size-msg->pos)); - pos+=(msg->size-msg->pos); - } - else - { - memcpy(buf,&msg->buf[pos],max); - pos+=max; - } + { + memcpy(buf,&msg->buf[msg->pos],(msg->size-msg->pos)); + bytes_read = msg->size-msg->pos; + msg->pos+=(msg->size-msg->pos); + } + else + { + memcpy(buf,&msg->buf[msg->pos],max); + msg->pos+=max; + bytes_read = max; + } - if (msg->pos==msg->size) - { - if (NULL!=msg->transmit_cont) - msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR); - res = remove_http_message(ps,msg); - } + if (msg->pos==msg->size) + { + if (NULL!=msg->transmit_cont) + msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,GNUNET_OK); + remove_http_message(ps,msg); + } } return bytes_read; } @@ -496,13 +876,13 @@ int server_read_callback (void *cls, uint64_t pos, char *buf, int max) * already exists and create a new one if not. */ static int -accessHandlerCallback (void *cls, - struct MHD_Connection *mhd_connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, - size_t * upload_data_size, void **httpSessionCache) +mdh_access_cb (void *cls, + struct MHD_Connection *mhd_connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t * upload_data_size, void **httpSessionCache) { struct Plugin *plugin = cls; struct MHD_Response *response; @@ -513,17 +893,19 @@ accessHandlerCallback (void *cls, char address[INET6_ADDRSTRLEN+14]; struct GNUNET_PeerIdentity pi_in; + size_t id_num = 0; struct IPv4HttpAddress ipv4addr; struct IPv6HttpAddress ipv6addr; struct HTTP_PeerContext *pc; - struct Session *ps; + struct Session *ps = NULL; + struct Session *ps_tmp = NULL; int res = GNUNET_NO; int send_error_to_client; - void * addr; - size_t addr_len; + void * addr = NULL; + size_t addr_len = 0 ; GNUNET_assert(cls !=NULL); send_error_to_client = GNUNET_NO; @@ -531,16 +913,31 @@ accessHandlerCallback (void *cls, if (NULL == *httpSessionCache) { /* check url for peer identity , if invalid send HTTP 404*/ - res = GNUNET_CRYPTO_hash_from_string ( &url[1], &(pi_in.hashPubKey)); + size_t len = strlen(&url[1]); + char * peer = GNUNET_malloc(104+1); + + if ((len>104) && (url[104]==';')) + { + char * id = GNUNET_malloc((len-104)+1); + strcpy(id,&url[105]); + memcpy(peer,&url[1],103); + peer[103] = '\0'; + id_num = strtoul ( id, NULL , 10); + GNUNET_free(id); + } + res = GNUNET_CRYPTO_hash_from_string (peer, &(pi_in.hashPubKey)); + GNUNET_free(peer); if ( GNUNET_SYSERR == res ) { response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO); res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response); MHD_destroy_response (response); +#if DEBUG_CONNECTIONS if (res == MHD_YES) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, sent HTTP 1.1/404\n"); else GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, could not send error\n"); +#endif return res; } } @@ -559,8 +956,14 @@ accessHandlerCallback (void *cls, { pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext)); pc->plugin = plugin; + pc->session_id_counter=1; + pc->last_session = NULL; memcpy(&pc->identity, &pi_in, sizeof(struct GNUNET_PeerIdentity)); GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP peers active"), + 1, + GNUNET_NO); } conn_info = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS ); @@ -573,7 +976,6 @@ accessHandlerCallback (void *cls, ipv4addr.u_port = addrin->sin_port; addr = &ipv4addr; addr_len = sizeof(struct IPv4HttpAddress); - //con = session_check_inbound_address (plugin, cs, (const void *) &ipv4addr, sizeof (struct IPv4HttpAddress)); } /* Incoming IPv6 connection */ if ( AF_INET6 == conn_info->client_addr->sin_family) @@ -585,8 +987,25 @@ accessHandlerCallback (void *cls, addr = &ipv6addr; addr_len = sizeof(struct IPv6HttpAddress); } - /* Set closure and update current session*/ - ps = get_Session(plugin, pc, addr, addr_len); + + GNUNET_assert (addr != NULL); + GNUNET_assert (addr_len != 0); + + ps = NULL; + /* only inbound sessions here */ + + ps_tmp = pc->head; + while (ps_tmp!=NULL) + { + if ((ps_tmp->direction==INBOUND) && (ps_tmp->session_id == id_num) && (id_num!=0)) + { + if ((ps_tmp->recv_force_disconnect!=GNUNET_YES) && (ps_tmp->send_force_disconnect!=GNUNET_YES)) + ps=ps_tmp; + break; + } + ps_tmp=ps_tmp->next; + } + if (ps==NULL) { ps = GNUNET_malloc(sizeof (struct Session)); @@ -601,26 +1020,47 @@ accessHandlerCallback (void *cls, ps->recv_connected=GNUNET_NO; ps->recv_active=GNUNET_NO; ps->peercontext=pc; - ps->url = create_url (plugin, ps->addr, ps->addrlen); + ps->session_id =id_num; + ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id); GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP inbound sessions for peers active"), + 1, + GNUNET_NO); } *httpSessionCache = ps; if (ps->msgtok==NULL) ps->msgtok = GNUNET_SERVER_mst_create (&mhd_write_mst_cb, ps); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n", +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n", + ps, method, GNUNET_i2s(&pc->identity), http_plugin_address_to_string(NULL, ps->addr, ps->addrlen)); +#endif } /* Is it a PUT or a GET request */ if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) { + if (ps->recv_force_disconnect == GNUNET_YES) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection was forced to disconnect\n",ps); +#endif + ps->recv_active = GNUNET_NO; + return MHD_NO; + } if ((*upload_data_size == 0) && (ps->recv_active==GNUNET_NO)) { + ps->recv_endpoint = mhd_connection; + ps->recv_connected = GNUNET_YES; ps->recv_active = GNUNET_YES; + ps->recv_force_disconnect = GNUNET_NO; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound PUT connection connected\n",ps); +#endif return MHD_YES; } @@ -629,7 +1069,9 @@ accessHandlerCallback (void *cls, { response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO); res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 200 OK as PUT Response\n",HTTP_PUT_RESPONSE, strlen (HTTP_PUT_RESPONSE), res ); +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Sent HTTP/1.1: 200 OK as PUT Response\n",ps); +#endif MHD_destroy_response (response); ps->recv_active=GNUNET_NO; return MHD_YES; @@ -647,34 +1089,39 @@ accessHandlerCallback (void *cls, } if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) ) { - response = MHD_create_response_from_callback(-1,32 * 1024, &server_read_callback, ps, NULL); - res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - return res; + if (ps->send_force_disconnect == GNUNET_YES) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection was forced to disconnect\n",ps); +#endif + ps->send_active = GNUNET_NO; + return MHD_NO; + } + ps->send_connected = GNUNET_YES; + ps->send_active = GNUNET_YES; + ps->send_endpoint = mhd_connection; + ps->send_force_disconnect = GNUNET_NO; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound GET connection connected\n",ps); +#endif + response = MHD_create_response_from_callback(-1,32 * 1024, &mhd_send_callback, ps, NULL); + res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + return MHD_YES; } return MHD_NO; } - -/** - * Call MHD to process pending ipv4 requests and then go back - * and schedule the next run. - */ -static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); -/** - * Call MHD to process pending ipv6 requests and then go back - * and schedule the next run. - */ -static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); - /** * Function that queries MHD's select sets and * starts the task waiting for them. + * @param plugin plugin + * @param daemon_handle the MHD daemon handle + * @return gnunet task identifier */ static GNUNET_SCHEDULER_TaskIdentifier -http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle) +http_server_daemon_prepare (struct Plugin *plugin , struct MHD_Daemon *daemon_handle) { - struct Plugin *plugin = cls; GNUNET_SCHEDULER_TaskIdentifier ret; fd_set rs; fd_set ws; @@ -687,7 +1134,6 @@ http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle) int haveto; struct GNUNET_TIME_Relative tv; - GNUNET_assert(cls !=NULL); ret = GNUNET_SCHEDULER_NO_TASK; FD_ZERO(&rs); FD_ZERO(&ws); @@ -712,6 +1158,12 @@ http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle) GNUNET_NETWORK_fdset_copy_native (wes, &es, max); if (daemon_handle == plugin->http_server_daemon_v4) { + if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4); + plugin->http_server_daemon_v4 = GNUNET_SCHEDULER_NO_TASK; + } + ret = GNUNET_SCHEDULER_add_select (plugin->env->sched, GNUNET_SCHEDULER_PRIORITY_DEFAULT, GNUNET_SCHEDULER_NO_TASK, @@ -723,6 +1175,12 @@ http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle) } if (daemon_handle == plugin->http_server_daemon_v6) { + if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6); + plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK; + } + ret = GNUNET_SCHEDULER_add_select (plugin->env->sched, GNUNET_SCHEDULER_PRIORITY_DEFAULT, GNUNET_SCHEDULER_NO_TASK, @@ -739,8 +1197,10 @@ http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle) } /** - * Call MHD to process pending requests and then go back + * Call MHD IPv4 to process pending requests and then go back * and schedule the next run. + * @param cls plugin as closure + * @param tc task context */ static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) @@ -748,21 +1208,21 @@ static void http_server_daemon_v4_run (void *cls, struct Plugin *plugin = cls; GNUNET_assert(cls !=NULL); - if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK) - plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK; + plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) return; GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4)); plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4); - return; -} + } /** - * Call MHD to process pending requests and then go back + * Call MHD IPv6 to process pending requests and then go back * and schedule the next run. + * @param cls plugin as closure + * @param tc task context */ static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) @@ -770,39 +1230,25 @@ static void http_server_daemon_v6_run (void *cls, struct Plugin *plugin = cls; GNUNET_assert(cls !=NULL); - if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) - plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK; + plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) return; GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6)); plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6); - return; } -/** - * Function setting up curl handle and selecting message to send - * @param cls plugin - * @param ses session to send data to - * @param con connection - * @return bytes sent to peer - */ -static ssize_t send_check_connections (void *cls, struct Session* ses , struct Session *ps); - - -static size_t curl_get_header_function( void *ptr, size_t size, size_t nmemb, void *stream) +static size_t curl_get_header_cb( void *ptr, size_t size, size_t nmemb, void *stream) { struct Session * ps = stream; - char * tmp; - size_t len = size * nmemb; long http_result = 0; int res; /* Getting last http result code */ + GNUNET_assert(NULL!=ps); if (ps->recv_connected==GNUNET_NO) { - GNUNET_assert(NULL!=ps); res = curl_easy_getinfo(ps->recv_endpoint, CURLINFO_RESPONSE_CODE, &http_result); if (CURLE_OK == res) { @@ -810,13 +1256,18 @@ static size_t curl_get_header_function( void *ptr, size_t size, size_t nmemb, vo { ps->recv_connected = GNUNET_YES; ps->recv_active = GNUNET_YES; +#if DEBUG_CONNECTIONS GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to recieve data\n",ps); +#endif // Calling send_check_connections again since receive is established - send_check_connections (ps->peercontext->plugin, NULL, ps); + send_check_connections (ps->peercontext->plugin, ps); } } } +#if DEBUG_CURL + char * tmp; + size_t len = size * nmemb; tmp = NULL; if ((size * nmemb) < SIZE_MAX) tmp = GNUNET_malloc (len+1); @@ -829,17 +1280,25 @@ static size_t curl_get_header_function( void *ptr, size_t size, size_t nmemb, vo if (tmp[len-2] == 13) tmp[len-2]= '\0'; } -#if DEBUG_HTTP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Header: `%s' %u \n",tmp, http_result); -#endif + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Header: %s\n",ps,tmp); } - if (NULL != tmp) - GNUNET_free (tmp); + GNUNET_free_non_null (tmp); +#endif return size * nmemb; } -static size_t curl_put_header_function( void *ptr, size_t size, size_t nmemb, void *stream) +/** + * Callback called by libcurl when new headers arrive + * Used to get HTTP result for curl operations + * @param ptr stream to read from + * @param size size of one char element + * @param nmemb number of char elements + * @param stream closure set by user + * @return bytes read by function + */ + +static size_t curl_put_header_cb( void *ptr, size_t size, size_t nmemb, void *stream) { struct Session * ps = stream; @@ -857,13 +1316,17 @@ static size_t curl_put_header_function( void *ptr, size_t size, size_t nmemb, vo { ps->send_connected = GNUNET_YES; ps->send_active = GNUNET_YES; +#if DEBUG_CONNECTIONS GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to send data\n",ps); +#endif } if ((http_result == 200) && (ps->send_connected==GNUNET_YES)) { ps->send_connected = GNUNET_NO; ps->send_active = GNUNET_NO; +#if DEBUG_CONNECTIONS GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: sending disconnected\n",ps); +#endif } } @@ -879,12 +1342,9 @@ static size_t curl_put_header_function( void *ptr, size_t size, size_t nmemb, vo if (tmp[len-2] == 13) tmp[len-2]= '\0'; } -#if DEBUG_HTTP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Header: `%s' %u \n",tmp, http_result); -#endif } - if (NULL != tmp) - GNUNET_free (tmp); + + GNUNET_free_non_null (tmp); return size * nmemb; } @@ -898,21 +1358,29 @@ static size_t curl_put_header_function( void *ptr, size_t size, size_t nmemb, vo * @param ptr source pointer, passed to the libcurl handle * @return bytes written to stream */ -static size_t send_curl_send_callback(void *stream, size_t size, size_t nmemb, void *ptr) +static size_t curl_send_cb(void *stream, size_t size, size_t nmemb, void *ptr) { struct Session * ps = ptr; struct HTTP_Message * msg = ps->pending_msgs_tail; size_t bytes_sent; size_t len; - if (ps->pending_msgs_tail == NULL) + if (ps->send_active == GNUNET_NO) + { + return CURL_READFUNC_PAUSE; + } + + if ((ps->pending_msgs_tail == NULL) && (ps->send_active == GNUNET_YES)) { +#if DEBUG_CONNECTIONS GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: No Message to send, pausing connection\n",ps); +#endif ps->send_active = GNUNET_NO; return CURL_READFUNC_PAUSE; } - msg = ps->pending_msgs_tail; + GNUNET_assert (msg!=NULL); + /* data to send */ if (msg->pos < msg->size) { @@ -940,15 +1408,42 @@ static size_t send_curl_send_callback(void *stream, size_t size, size_t nmemb, v if ( msg->pos == msg->size) { +#if DEBUG_CONNECTIONS GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Message with %u bytes sent, removing message from queue \n",ps, msg->pos); +#endif /* Calling transmit continuation */ - if (( NULL != ps->pending_msgs_tail) && (NULL != ps->pending_msgs_tail->transmit_cont)) + if (NULL != ps->pending_msgs_tail->transmit_cont) msg->transmit_cont (ps->pending_msgs_tail->transmit_cont_cls,&(ps->peercontext)->identity,GNUNET_OK); remove_http_message(ps, msg); } return bytes_sent; } +static void curl_receive_mst_cb (void *cls, + void *client, + const struct GNUNET_MessageHeader *message) +{ + struct Session *ps = cls; + GNUNET_assert(ps != NULL); + + struct HTTP_PeerContext *pc = ps->peercontext; + GNUNET_assert(pc != NULL); +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n", + ps, + ntohs(message->type), + ntohs(message->size), + GNUNET_i2s(&(pc->identity)),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen)); +#endif + pc->plugin->env->receive (pc->plugin->env->cls, + &pc->identity, + message, 1, ps, + ps->addr, + ps->addrlen); +} + + /** * Callback method used with libcurl * Method is called when libcurl needs to write data during sending @@ -958,309 +1453,187 @@ static size_t send_curl_send_callback(void *stream, size_t size, size_t nmemb, v * @param ptr destination pointer, passed to the libcurl handle * @return bytes read from stream */ -static size_t send_curl_receive_callback( void *stream, size_t size, size_t nmemb, void *ptr) +static size_t curl_receive_cb( void *stream, size_t size, size_t nmemb, void *ptr) { struct Session * ps = ptr; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: %u bytes recieved\n",ps, size*nmemb); +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: %u bytes received\n",ps, size*nmemb); +#endif GNUNET_SERVER_mst_receive(ps->msgtok, ps, stream, size*nmemb, GNUNET_NO, GNUNET_NO); - return (size * nmemb); } -/** - * Function setting up file descriptors and scheduling task to run - * @param cls closure - * @param ses session to send data to - * @return bytes sent to peer - */ -static size_t send_schedule(void *cls, struct Session* ses ); - +static void curl_handle_finished (struct Plugin *plugin) +{ + struct Session *ps = NULL; + struct HTTP_PeerContext *pc = NULL; + struct CURLMsg *msg; + struct HTTP_Message * cur_msg = NULL; + + int msgs_in_queue; + char * tmp; + long http_result; + + do + { + msg = curl_multi_info_read (plugin->multi_handle, &msgs_in_queue); + if ((msgs_in_queue == 0) || (msg == NULL)) + break; + /* get session for affected curl handle */ + GNUNET_assert ( msg->easy_handle != NULL ); + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &tmp); + ps = (struct Session *) tmp; + GNUNET_assert ( ps != NULL ); + pc = ps->peercontext; + GNUNET_assert ( pc != NULL ); + switch (msg->msg) + { + + case CURLMSG_DONE: + if ( (msg->data.result != CURLE_OK) && + (msg->data.result != CURLE_GOT_NOTHING) ) + { + /* sending msg failed*/ + if (msg->easy_handle == ps->send_endpoint) + { + #if DEBUG_CONNECTIONS + GNUNET_log(GNUNET_ERROR_TYPE_INFO, + _("Connection %X: HTTP PUT to peer `%s' (`%s') failed: `%s' `%s'\n"), + ps, + GNUNET_i2s(&pc->identity), + http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), + "curl_multi_perform", + curl_easy_strerror (msg->data.result)); + #endif + ps->send_connected = GNUNET_NO; + ps->send_active = GNUNET_NO; + curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint); + //curl_easy_cleanup(ps->send_endpoint); + //ps->send_endpoint=NULL; + cur_msg = ps->pending_msgs_tail; + if (( NULL != cur_msg) && ( NULL != cur_msg->transmit_cont)) + cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR); + } + /* GET connection failed */ + if (msg->easy_handle == ps->recv_endpoint) + { + #if DEBUG_CONNECTIONS + GNUNET_log(GNUNET_ERROR_TYPE_INFO, + _("Connection %X: HTTP GET to peer `%s' (`%s') failed: `%s' `%s'\n"), + ps, + GNUNET_i2s(&pc->identity), + http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), + "curl_multi_perform", + curl_easy_strerror (msg->data.result)); + #endif + ps->recv_connected = GNUNET_NO; + ps->recv_active = GNUNET_NO; + curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint); + //curl_easy_cleanup(ps->recv_endpoint); + //ps->recv_endpoint=NULL; + } + } + else + { + if (msg->easy_handle == ps->send_endpoint) + { + GNUNET_assert (CURLE_OK == curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_result)); + #if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection %X: HTTP PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n", + ps, + GNUNET_i2s(&pc->identity), + http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), + http_result); + #endif + /* Calling transmit continuation */ + cur_msg = ps->pending_msgs_tail; + if (( NULL != cur_msg) && (NULL != cur_msg->transmit_cont)) + { + /* HTTP 1xx : Last message before here was informational */ + if ((http_result >=100) && (http_result < 200)) + cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK); + /* HTTP 2xx: successful operations */ + if ((http_result >=200) && (http_result < 300)) + cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK); + /* HTTP 3xx..5xx: error */ + if ((http_result >=300) && (http_result < 600)) + cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR); + } + ps->send_connected = GNUNET_NO; + ps->send_active = GNUNET_NO; + curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint); + //curl_easy_cleanup(ps->send_endpoint); + //ps->send_endpoint =NULL; + } + if (msg->easy_handle == ps->recv_endpoint) + { + #if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n", + ps, + GNUNET_i2s(&pc->identity), + http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), + http_result); + #endif + ps->recv_connected = GNUNET_NO; + ps->recv_active = GNUNET_NO; + curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint); + //curl_easy_cleanup(ps->recv_endpoint); + //ps->recv_endpoint=NULL; + } + } + if ((ps->recv_connected == GNUNET_NO) && (ps->send_connected == GNUNET_NO)) + remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR); + break; + default: + break; + } + } + while ( (msgs_in_queue > 0) ); +} /** - * Function setting up curl handle and selecting message to send - * @param cls plugin - * @param ses session to send data to - * @param con connection - * @return bytes sent to peer + * Task performing curl operations + * @param cls plugin as closure + * @param tc gnunet scheduler task context */ -static ssize_t send_check_connections (void *cls, struct Session* ses , struct Session *ps) -{ - struct Plugin *plugin = cls; - int bytes_sent = 0; - CURLMcode mret; - struct HTTP_Message * msg; - struct GNUNET_TIME_Relative timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT; - - GNUNET_assert(cls !=NULL); - - if (ps->direction == OUTBOUND) - { - /* RECV DIRECTION */ - /* Check if session is connected to receive data, otherwise connect to peer */ - if (ps->recv_connected == GNUNET_NO) - { - if (ps->recv_endpoint == NULL) - { - ps->recv_endpoint = curl_easy_init(); -#if DEBUG_CURL - curl_easy_setopt(ps->recv_endpoint, CURLOPT_VERBOSE, 1L); -#endif - curl_easy_setopt(ps->recv_endpoint, CURLOPT_URL, ps->url); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_HEADERFUNCTION, &curl_get_header_function); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEHEADER, ps); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_READFUNCTION, send_curl_send_callback); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_READDATA, ps); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEFUNCTION, send_curl_receive_callback); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEDATA, ps); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_TIMEOUT, (long) timeout.value); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_PRIVATE, ps); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); - curl_easy_setopt(ps->recv_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE); - - mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint); - if (mret != CURLM_OK) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("%s failed at %s:%d: `%s'\n"), - "curl_multi_add_handle", __FILE__, __LINE__, - curl_multi_strerror (mret)); - return -1; - } - bytes_sent = send_schedule (plugin, NULL); - if (ps->msgtok != NULL) - ps->msgtok = GNUNET_SERVER_mst_create (&curl_write_mst_cb, ps); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound not connected, initiating connection\n",ps); - } - } - - /* waiting for receive direction */ - if (ps->recv_connected==GNUNET_NO) - return 0; - - /* SEND DIRECTION */ - /* Check if session is connected to send data, otherwise connect to peer */ - if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint!= NULL)) - { - if (ps->send_active == GNUNET_YES) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound active, enqueueing message\n",ps); - return bytes_sent; - } - if (ps->send_active == GNUNET_NO) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",ps); - curl_easy_pause(ps->send_endpoint,CURLPAUSE_CONT); - ps->send_active=GNUNET_YES; - return bytes_sent; - } - } - /* not connected, initiate connection */ - if ((ps->send_connected==GNUNET_NO) && (NULL == ps->send_endpoint)) - ps->send_endpoint = curl_easy_init(); - GNUNET_assert (ps->send_endpoint != NULL); - GNUNET_assert (NULL != ps->pending_msgs_tail); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound not connected, initiating connection\n",ps); - ps->send_active = GNUNET_NO; - msg = ps->pending_msgs_tail; - - #if DEBUG_CURL - curl_easy_setopt(ps->send_endpoint, CURLOPT_VERBOSE, 1L); - #endif - curl_easy_setopt(ps->send_endpoint, CURLOPT_URL, ps->url); - curl_easy_setopt(ps->send_endpoint, CURLOPT_PUT, 1L); - curl_easy_setopt(ps->send_endpoint, CURLOPT_HEADERFUNCTION, &curl_put_header_function); - curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEHEADER, ps); - curl_easy_setopt(ps->send_endpoint, CURLOPT_READFUNCTION, send_curl_send_callback); - curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps); - curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEFUNCTION, send_curl_receive_callback); - curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps); - curl_easy_setopt(ps->send_endpoint, CURLOPT_TIMEOUT, (long) timeout.value); - curl_easy_setopt(ps->send_endpoint, CURLOPT_PRIVATE, ps); - curl_easy_setopt(ps->send_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); - curl_easy_setopt(ps->send_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE); - - mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint); - if (mret != CURLM_OK) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("%s failed at %s:%d: `%s'\n"), - "curl_multi_add_handle", __FILE__, __LINE__, - curl_multi_strerror (mret)); - return -1; - } - bytes_sent = send_schedule (plugin, NULL); - return bytes_sent; - } - if (ps->direction == INBOUND) - { - GNUNET_assert (NULL != ps->pending_msgs_tail); - msg = ps->pending_msgs_tail; - if ((ps->recv_connected==GNUNET_YES) && (ps->recv_connected==GNUNET_YES)) - bytes_sent = msg->size; - return bytes_sent; - } - return 0; -} - -static void send_execute (void *cls, +static void curl_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct Plugin *plugin = cls; static unsigned int handles_last_run; int running; - struct CURLMsg *msg; CURLMcode mret; - struct Session *ps = NULL; - struct HTTP_PeerContext *pc = NULL; - struct HTTP_Message * cur_msg = NULL; - long http_result; GNUNET_assert(cls !=NULL); - plugin->http_server_task_send = GNUNET_SCHEDULER_NO_TASK; + + plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) return; - do { running = 0; mret = curl_multi_perform (plugin->multi_handle, &running); - if (running < handles_last_run) - { - do - { - - msg = curl_multi_info_read (plugin->multi_handle, &running); - if (msg == NULL) - break; - /* get session for affected curl handle */ - GNUNET_assert ( msg->easy_handle != NULL ); - curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char *) &ps); - GNUNET_assert ( ps != NULL ); - pc = ps->peercontext; - GNUNET_assert ( pc != NULL ); - switch (msg->msg) - { - - case CURLMSG_DONE: - if ( (msg->data.result != CURLE_OK) && - (msg->data.result != CURLE_GOT_NOTHING) ) - { - /* sending msg failed*/ - if (msg->easy_handle == ps->send_endpoint) - { - GNUNET_log(GNUNET_ERROR_TYPE_INFO, - _("Connection %X: HTTP PUT to peer `%s' (`%s') failed: `%s' `%s'\n"), - ps, - GNUNET_i2s(&pc->identity), - http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), - "curl_multi_perform", - curl_easy_strerror (msg->data.result)); - - ps->send_connected = GNUNET_NO; - ps->send_active = GNUNET_NO; - curl_multi_remove_handle(plugin->multi_handle,msg->easy_handle); - curl_easy_cleanup(ps->send_endpoint); - ps->send_endpoint=NULL; - cur_msg = ps->pending_msgs_tail; - if (( NULL != cur_msg) && ( NULL != cur_msg->transmit_cont)) - cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR); - } - /* GET connection failed */ - if (msg->easy_handle == ps->recv_endpoint) - { - GNUNET_log(GNUNET_ERROR_TYPE_INFO, - _("Connection %X: HTTP GET to peer `%s' (`%s') failed: `%s' `%s'\n"), - ps, - GNUNET_i2s(&pc->identity), - http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), - "curl_multi_perform", - curl_easy_strerror (msg->data.result)); - ps->recv_connected = GNUNET_NO; - ps->recv_active = GNUNET_NO; - curl_multi_remove_handle(plugin->multi_handle,msg->easy_handle); - curl_easy_cleanup(ps->recv_endpoint); - ps->recv_endpoint=NULL; - } - } - else - { - if (msg->easy_handle == ps->send_endpoint) - { - GNUNET_assert (CURLE_OK == curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_result)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connection %X: HTTP PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n", - ps, - GNUNET_i2s(&pc->identity), - http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), - http_result); - - /* Calling transmit continuation */ - cur_msg = ps->pending_msgs_tail; - if (( NULL != cur_msg) && (NULL != cur_msg->transmit_cont)) - { - /* HTTP 1xx : Last message before here was informational */ - if ((http_result >=100) && (http_result < 200)) - cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK); - /* HTTP 2xx: successful operations */ - if ((http_result >=200) && (http_result < 300)) - cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK); - /* HTTP 3xx..5xx: error */ - if ((http_result >=300) && (http_result < 600)) - cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR); - } - ps->send_connected = GNUNET_NO; - ps->send_active = GNUNET_NO; - curl_multi_remove_handle(plugin->multi_handle,msg->easy_handle); - curl_easy_cleanup(ps->send_endpoint); - ps->send_endpoint =NULL; - } - if (msg->easy_handle == ps->recv_endpoint) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n", - ps, - GNUNET_i2s(&pc->identity), - http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), - http_result); - - ps->recv_connected = GNUNET_NO; - ps->recv_active = GNUNET_NO; - curl_multi_remove_handle(plugin->multi_handle,msg->easy_handle); - curl_easy_cleanup(ps->recv_endpoint); - ps->recv_endpoint=NULL; - } - } - if (ps->pending_msgs_tail != NULL) - { - if (ps->pending_msgs_tail->pos>0) - remove_http_message(ps, ps->pending_msgs_tail); - } - return; - default: - break; - } - - } - while ( (running > 0) ); - } + if ((running < handles_last_run) && (running>0)) + curl_handle_finished(plugin); handles_last_run = running; } while (mret == CURLM_CALL_MULTI_PERFORM); - send_schedule(plugin, cls); + curl_schedule(plugin); } /** * Function setting up file descriptors and scheduling task to run - * @param ses session to send data to - * @return bytes sent to peer + * + * @param cls plugin as closure + * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok */ -static size_t send_schedule(void *cls, struct Session* ses ) +static int curl_schedule(struct Plugin *plugin) { - struct Plugin *plugin = cls; fd_set rs; fd_set ws; fd_set es; @@ -1270,7 +1643,13 @@ static size_t send_schedule(void *cls, struct Session* ses ) long to; CURLMcode mret; - GNUNET_assert(cls !=NULL); + /* Cancel previous scheduled task */ + if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task); + plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK; + } + max = -1; FD_ZERO (&rs); FD_ZERO (&ws); @@ -1282,7 +1661,7 @@ static size_t send_schedule(void *cls, struct Session* ses ) _("%s failed at %s:%d: `%s'\n"), "curl_multi_fdset", __FILE__, __LINE__, curl_multi_strerror (mret)); - return -1; + return GNUNET_SYSERR; } mret = curl_multi_timeout (plugin->multi_handle, &to); if (mret != CURLM_OK) @@ -1291,28 +1670,355 @@ static size_t send_schedule(void *cls, struct Session* ses ) _("%s failed at %s:%d: `%s'\n"), "curl_multi_timeout", __FILE__, __LINE__, curl_multi_strerror (mret)); - return -1; + return GNUNET_SYSERR; } grs = GNUNET_NETWORK_fdset_create (); gws = GNUNET_NETWORK_fdset_create (); GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1); GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1); - plugin->http_server_task_send = GNUNET_SCHEDULER_add_select (plugin->env->sched, + plugin->http_curl_task = GNUNET_SCHEDULER_add_select (plugin->env->sched, GNUNET_SCHEDULER_PRIORITY_DEFAULT, GNUNET_SCHEDULER_NO_TASK, - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0), + (to == -1) ? GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to), grs, gws, - &send_execute, + &curl_perform, plugin); GNUNET_NETWORK_fdset_destroy (gws); GNUNET_NETWORK_fdset_destroy (grs); + return GNUNET_OK; +} + +/** + * Function to log curl debug messages with GNUNET_log + * @param curl handle + * @param type curl_infotype + * @param data data + * @param size size + * @param cls closure + * @return 0 + */ +int curl_logger (CURL * curl, curl_infotype type , char * data, size_t size , void * cls) +{ + char * text = GNUNET_malloc(size+2); + if (type == CURLINFO_TEXT) + { + memcpy(text,data,size); + if (text[size-1] == '\n') + text[size] = '\0'; + else + { + text[size] = '\n'; + text[size+1] = '\0'; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"CURL: Connection %X - %s", cls, text); + GNUNET_free(text); + } + return 0; +} + +/** + * Function setting up curl handle and selecting message to send + * + * @param plugin plugin + * @param ps session + * @return GNUNET_SYSERR on failure, GNUNET_NO if connecting, GNUNET_YES if ok + */ +static int send_check_connections (struct Plugin *plugin, struct Session *ps) +{ + CURLMcode mret; + struct HTTP_Message * msg; + + struct GNUNET_TIME_Relative timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT; + + if (ps->direction == OUTBOUND) + { + /* RECV DIRECTION */ + /* Check if session is connected to receive data, otherwise connect to peer */ + if (ps->recv_connected == GNUNET_NO) + { + int fresh = GNUNET_NO; + if (ps->recv_endpoint == NULL) + { + fresh = GNUNET_YES; + ps->recv_endpoint = curl_easy_init(); + } +#if DEBUG_CURL + curl_easy_setopt(ps->recv_endpoint, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_DEBUGFUNCTION , &curl_logger); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_DEBUGDATA , ps->recv_endpoint); +#endif +#if BUILD_HTTPS + curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_SSL_VERIFYHOST, 0); +#endif + curl_easy_setopt(ps->recv_endpoint, CURLOPT_URL, ps->url); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_HEADERFUNCTION, &curl_get_header_cb); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEHEADER, ps); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_READFUNCTION, curl_send_cb); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_READDATA, ps); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEDATA, ps); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_TIMEOUT, (long) timeout.value); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_PRIVATE, ps); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_BUFFERSIZE, 2*GNUNET_SERVER_MAX_MESSAGE_SIZE); +#if CURL_TCP_NODELAY + curl_easy_setopt(ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1); +#endif + + if (fresh==GNUNET_YES) + { + mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Connection: %X: %s failed at %s:%d: `%s'\n"), + ps, + "curl_multi_add_handle", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return GNUNET_SYSERR; + } + } + if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task); + plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK; + } + plugin->http_curl_task = GNUNET_SCHEDULER_add_now (plugin->env->sched, &curl_perform, plugin); + } + + /* waiting for receive direction */ + if (ps->recv_connected==GNUNET_NO) + return GNUNET_NO; + + /* SEND DIRECTION */ + /* Check if session is connected to send data, otherwise connect to peer */ + if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint!= NULL)) + { + if (ps->send_active == GNUNET_YES) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound active, enqueueing message\n",ps); +#endif + return GNUNET_YES; + } + if (ps->send_active == GNUNET_NO) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",ps); +#endif + if (CURLE_OK == curl_easy_pause(ps->send_endpoint,CURLPAUSE_CONT)) + { + ps->send_active=GNUNET_YES; + if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task); + plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK; + } + plugin->http_curl_task = GNUNET_SCHEDULER_add_now (plugin->env->sched, &curl_perform, plugin); + return GNUNET_YES; + } + else + return GNUNET_SYSERR; + } + } + /* not connected, initiate connection */ + if (ps->send_connected==GNUNET_NO) + { + int fresh = GNUNET_NO; + if (NULL == ps->send_endpoint) + { + ps->send_endpoint = curl_easy_init(); + fresh = GNUNET_YES; + } + GNUNET_assert (ps->send_endpoint != NULL); + GNUNET_assert (NULL != ps->pending_msgs_tail); +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound not connected, initiating connection\n",ps); +#endif + ps->send_active = GNUNET_NO; + msg = ps->pending_msgs_tail; + +#if DEBUG_CURL + curl_easy_setopt(ps->send_endpoint, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(ps->send_endpoint, CURLOPT_DEBUGFUNCTION , &curl_logger); + curl_easy_setopt(ps->send_endpoint, CURLOPT_DEBUGDATA , ps->send_endpoint); +#endif +#if BUILD_HTTPS + curl_easy_setopt (ps->send_endpoint, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); + curl_easy_setopt(ps->send_endpoint, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(ps->send_endpoint, CURLOPT_SSL_VERIFYHOST, 0); +#endif + curl_easy_setopt(ps->send_endpoint, CURLOPT_URL, ps->url); + curl_easy_setopt(ps->send_endpoint, CURLOPT_PUT, 1L); + curl_easy_setopt(ps->send_endpoint, CURLOPT_HEADERFUNCTION, &curl_put_header_cb); + curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEHEADER, ps); + curl_easy_setopt(ps->send_endpoint, CURLOPT_READFUNCTION, curl_send_cb); + curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps); + curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb); + curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps); + curl_easy_setopt(ps->send_endpoint, CURLOPT_TIMEOUT, (long) timeout.value); + curl_easy_setopt(ps->send_endpoint, CURLOPT_PRIVATE, ps); + curl_easy_setopt(ps->send_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); + curl_easy_setopt(ps->send_endpoint, CURLOPT_BUFFERSIZE, 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE); +#if CURL_TCP_NODELAY + curl_easy_setopt(ps->send_endpoint, CURLOPT_TCP_NODELAY, 1); +#endif - /* FIXME: return bytes REALLY sent */ - return 0; + if (fresh==GNUNET_YES) + { + mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Connection: %X: %s failed at %s:%d: `%s'\n"), + ps, + "curl_multi_add_handle", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return GNUNET_SYSERR; + } + } + } + if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task); + plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK; + } + plugin->http_curl_task = GNUNET_SCHEDULER_add_now (plugin->env->sched, &curl_perform, plugin); + return GNUNET_YES; + } + if (ps->direction == INBOUND) + { + GNUNET_assert (NULL != ps->pending_msgs_tail); + if ((ps->recv_connected==GNUNET_YES) && (ps->send_connected==GNUNET_YES) && + (ps->recv_force_disconnect==GNUNET_NO) && (ps->recv_force_disconnect==GNUNET_NO)) + return GNUNET_YES; + } + return GNUNET_SYSERR; } +/** + * select best session to transmit data to peer + * + * @param cls closure + * @param pc peer context of target peer + * @param addr address of target peer + * @param addrlen address length + * @param force_address does transport service enforce address? + * @param session session passed by transport service + * @return selected session + * + */ +static struct Session * send_select_session (struct HTTP_PeerContext *pc, const void * addr, size_t addrlen, int force_address, struct Session * session) +{ + struct Session * tmp = NULL; + int addr_given = GNUNET_NO; + + if ((addr!=NULL) && (addrlen>0)) + addr_given = GNUNET_YES; + + if (force_address == GNUNET_YES) + { + /* check session given as argument */ + if ((session != NULL) && (addr_given == GNUNET_YES)) + { + if (0 == memcmp(session->addr, addr, addrlen)) + { + /* connection can not be used, since it is disconnected */ + if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send to forced address \n", session); +#endif + return session; + } + } + } + /* check last session used */ + if ((pc->last_session != NULL)&& (addr_given == GNUNET_YES)) + { + if (0 == memcmp(pc->last_session->addr, addr, addrlen)) + { + /* connection can not be used, since it is disconnected */ + if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session used to send to forced address \n", pc->last_session); +#endif + return pc->last_session; + } + } + } + /* find session in existing sessions */ + tmp = pc->head; + while ((tmp!=NULL) && (addr_given == GNUNET_YES)) + { + + if (0 == memcmp(tmp->addr, addr, addrlen)) + { + /* connection can not be used, since it is disconnected */ + if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to forced address \n", session); +#endif + return session; + } + + } + tmp=tmp->next; + } + /* no session to use */ + return NULL; + } + if ((force_address == GNUNET_NO) || (force_address == GNUNET_SYSERR)) + { + /* check session given as argument */ + if (session != NULL) + { + /* connection can not be used, since it is disconnected */ + if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send not-forced address \n", session); +#endif + return session; + } + + } + /* check last session used */ + if (pc->last_session != NULL) + { + /* connection can not be used, since it is disconnected */ + if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session to send to not-forced address \n", pc->last_session); +#endif + return pc->last_session; + } + } + /* find session in existing sessions */ + tmp = pc->head; + while (tmp!=NULL) + { + /* connection can not be used, since it is disconnected */ + if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to not-forced address \n", tmp); +#endif + return tmp; + } + tmp=tmp->next; + } + return NULL; + } + return NULL; +} /** * Function that can be used by the transport service to transmit @@ -1328,7 +2034,7 @@ static size_t send_schedule(void *cls, struct Session* ses ) * @param msgbuf_size number of bytes in 'msgbuf' * @param priority how important is the message (most plugins will * ignore message priority and just FIFO) - * @param timeout how long to wait at most for the transmission (does not + * @param to how long to wait at most for the transmission (does not * require plugins to discard the message after the timeout, * just advisory for the desired delay; most plugins will ignore * this as well) @@ -1366,12 +2072,29 @@ http_plugin_send (void *cls, { struct Plugin *plugin = cls; struct HTTP_Message *msg; - struct HTTP_PeerContext * pc; - struct Session * ps; + struct Session * ps = NULL; GNUNET_assert(cls !=NULL); - GNUNET_assert ((addr!=NULL) && (addrlen != 0)); + +#if DEBUG_HTTP + char * force; + if (force_address == GNUNET_YES) + GNUNET_asprintf(&force, "forced addr."); + if (force_address == GNUNET_NO) + GNUNET_asprintf(&force, "any addr."); + if (force_address == GNUNET_SYSERR) + GNUNET_asprintf(&force,"reliable bi-direc. address addr."); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport tells me to send %u bytes to `%s' using %s (%s) and session: %X\n", + msgbuf_size, + GNUNET_i2s(target), + force, + http_plugin_address_to_string(NULL, addr, addrlen), + session); + + GNUNET_free(force); +#endif pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey); /* Peer unknown */ @@ -1379,75 +2102,69 @@ http_plugin_send (void *cls, { pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext)); pc->plugin = plugin; + pc->session_id_counter=1; + pc->last_session = NULL; memcpy(&pc->identity, target, sizeof(struct GNUNET_PeerIdentity)); GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP peers active"), + 1, + GNUNET_NO); } - ps = get_Session(plugin, pc, addr, addrlen); + + ps = send_select_session (pc, addr, addrlen, force_address, session); + /* session not existing, but address forced -> creating new session */ - if ((ps==NULL) && (force_address == GNUNET_YES)) - { - ps = GNUNET_malloc(sizeof (struct Session)); - ps->addr = GNUNET_malloc(addrlen); - memcpy(ps->addr,addr,addrlen); - ps->addrlen = addrlen; - ps->direction=OUTBOUND; - ps->recv_connected = GNUNET_NO; - ps->send_connected = GNUNET_NO; - ps->pending_msgs_head = NULL; - ps->pending_msgs_tail = NULL; - ps->peercontext=pc; - ps->url = create_url (plugin, ps->addr, ps->addrlen); - GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps); - } - /* session not existing, address not forced -> looking for other session */ - if ((ps==NULL) && (force_address == GNUNET_NO)) - { - /* FIXME: CREATING SESSION, SHOULD CHOOSE EXISTING */ - ps = GNUNET_malloc(sizeof (struct Session)); - ps->addr = GNUNET_malloc(addrlen); - memcpy(ps->addr,addr,addrlen); - ps->addrlen = addrlen; - ps->direction=OUTBOUND; - ps->recv_connected = GNUNET_NO; - ps->send_connected = GNUNET_NO; - ps->pending_msgs_head = NULL; - ps->pending_msgs_tail = NULL; - ps->peercontext=pc; - ps->url = create_url (plugin, ps->addr, ps->addrlen); - GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps); - } - if ((ps==NULL) && (force_address == GNUNET_SYSERR)) + if (ps==NULL) { - /* FIXME: CREATING SESSION, SHOULD CHOOSE EXISTING */ - ps = GNUNET_malloc(sizeof (struct Session)); - ps->addr = GNUNET_malloc(addrlen); - memcpy(ps->addr,addr,addrlen); - ps->addrlen = addrlen; - ps->direction=OUTBOUND; - ps->recv_connected = GNUNET_NO; - ps->send_connected = GNUNET_NO; - ps->pending_msgs_head = NULL; - ps->pending_msgs_tail = NULL; - ps->peercontext=pc; - ps->url = create_url (plugin, ps->addr, ps->addrlen); - GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps); + if ((addr!=NULL) && (addrlen!=0)) + { + ps = GNUNET_malloc(sizeof (struct Session)); +#if DEBUG_SESSION_SELECTION + if (force_address == GNUNET_YES) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection & forced address: creating new session %X to peer %s\n", ps, GNUNET_i2s(target)); + if (force_address != GNUNET_YES) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection: creating new session %X to peer %s\n", ps, GNUNET_i2s(target)); +#endif + if ((addrlen!=0) && (addr!=NULL)) + { + ps->addr = GNUNET_malloc(addrlen); + memcpy(ps->addr,addr,addrlen); + ps->addrlen = addrlen; + } + else + { + ps->addr = NULL; + ps->addrlen = 0; + } + ps->direction=OUTBOUND; + ps->recv_connected = GNUNET_NO; + ps->recv_force_disconnect = GNUNET_NO; + ps->send_connected = GNUNET_NO; + ps->send_force_disconnect = GNUNET_NO; + ps->pending_msgs_head = NULL; + ps->pending_msgs_tail = NULL; + ps->peercontext=pc; + ps->session_id = pc->session_id_counter; + pc->session_id_counter++; + ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id); + if (ps->msgtok == NULL) + ps->msgtok = GNUNET_SERVER_mst_create (&curl_receive_mst_cb, ps); + GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP outbound sessions for peers active"), + 1, + GNUNET_NO); + } + else + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing session found & and no address given: no way to send this message to peer `%s'!\n", GNUNET_i2s(target)); +#endif + return GNUNET_SYSERR; + } } - char * force = GNUNET_malloc(30); - if (force_address == GNUNET_YES) - strcpy(force,"forced addr."); - if (force_address == GNUNET_NO) - strcpy(force,"any addr."); - if (force_address == GNUNET_SYSERR) - strcpy(force,"reliable bi-direc. address addr."); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport tells me to send %u bytes to `%s' %s (%s), session: %X\n", - msgbuf_size, - GNUNET_i2s(&pc->identity), - force, - http_plugin_address_to_string(NULL, addr, addrlen), - ps); - - GNUNET_free(force); /* create msg */ msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size); msg->next = NULL; @@ -1459,7 +2176,14 @@ http_plugin_send (void *cls, memcpy (msg->buf,msgbuf, msgbuf_size); GNUNET_CONTAINER_DLL_insert(ps->pending_msgs_head,ps->pending_msgs_tail,msg); - return send_check_connections (plugin, session, ps); + if (send_check_connections (plugin, ps) == GNUNET_SYSERR) + return GNUNET_SYSERR; + if (force_address != GNUNET_YES) + pc->last_session = ps; + + if (pc->last_session==NULL) + pc->last_session = ps; + return msg->size; } @@ -1477,48 +2201,53 @@ http_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) { + struct Plugin *plugin = cls; struct HTTP_PeerContext *pc = NULL; struct Session *ps = NULL; + //struct Session *tmp = NULL; pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey); if (pc==NULL) return; - ps = pc->head; while (ps!=NULL) { + /* Telling transport that session is getting disconnected */ + plugin->env->session_end(plugin, target, ps); if (ps->direction==OUTBOUND) { if (ps->send_endpoint!=NULL) { - curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint); - curl_easy_cleanup(ps->send_endpoint); - ps->send_endpoint=NULL; + //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint)); + //curl_easy_cleanup(ps->send_endpoint); + //ps->send_endpoint=NULL; + ps->send_force_disconnect = GNUNET_YES; } if (ps->recv_endpoint!=NULL) { - curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint); - curl_easy_cleanup(ps->recv_endpoint); - ps->recv_endpoint=NULL; + //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint)); + //curl_easy_cleanup(ps->recv_endpoint); + //ps->recv_endpoint=NULL; + ps->recv_force_disconnect = GNUNET_YES; } } + if (ps->direction==INBOUND) { - + ps->recv_force_disconnect = GNUNET_YES; + ps->send_force_disconnect = GNUNET_YES; } + while (ps->pending_msgs_head!=NULL) { remove_http_message(ps, ps->pending_msgs_head); } - ps->recv_connected = GNUNET_NO; ps->recv_active = GNUNET_NO; - ps->send_connected = GNUNET_NO; ps->send_active = GNUNET_NO; ps=ps->next; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"All connections to peer `%s' terminated\n", GNUNET_i2s(target)); } @@ -1579,11 +2308,11 @@ http_plugin_address_pretty_printer (void *cls, asc (asc_cls, NULL); return; } - res = GNUNET_asprintf(&ret,"http://%s:%u/",address,port); + res = GNUNET_asprintf(&ret,"%s://%s:%u/", PROTOCOL_PREFIX, address, port); GNUNET_free (address); GNUNET_assert(res != 0); - asc (asc_cls, ret); + GNUNET_free_non_null (ret); } @@ -1618,17 +2347,18 @@ http_plugin_address_suggested (void *cls, if (addrlen == sizeof (struct IPv4HttpAddress)) { v4 = (struct IPv4HttpAddress *) addr; + /* Not skipping loopback if (INADDR_LOOPBACK == ntohl(v4->ipv4_addr)) { return GNUNET_SYSERR; - } + } */ port = ntohs (v4->u_port); if (port != plugin->port_inbound) { return GNUNET_SYSERR; } } - else + if (addrlen == sizeof (struct IPv6HttpAddress)) { v6 = (struct IPv6HttpAddress *) addr; if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr)) @@ -1641,6 +2371,7 @@ http_plugin_address_suggested (void *cls, return GNUNET_SYSERR; } } + return GNUNET_OK; } @@ -1697,119 +2428,29 @@ http_plugin_address_to_string (void *cls, return ret; } -/** - * Add the IP of our network interface to the list of - * our external IP addresses. - * - * @param cls the 'struct Plugin*' - * @param name name of the interface - * @param isDefault do we think this may be our default interface - * @param addr address of the interface - * @param addrlen number of bytes in addr - * @return GNUNET_OK to continue iterating - */ -static int -process_interfaces (void *cls, - const char *name, - int isDefault, - const struct sockaddr *addr, socklen_t addrlen) -{ - struct Plugin *plugin = cls; - struct IPv4HttpAddress * t4; - struct IPv6HttpAddress * t6; - int af; - - GNUNET_assert(cls !=NULL); - af = addr->sa_family; - if (af == AF_INET) - { - t4 = GNUNET_malloc(sizeof(struct IPv4HttpAddress)); - if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr)) - { - /* skip loopback addresses */ - return GNUNET_OK; - } - t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; - t4->u_port = htons (plugin->port_inbound); - plugin->env->notify_address(plugin->env->cls,"http",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL); - - } - else if (af == AF_INET6) - { - t6 = GNUNET_malloc(sizeof(struct IPv6HttpAddress)); - if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr)) - { - /* skip link local addresses */ - return GNUNET_OK; - } - if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr)) - { - /* skip loopback addresses */ - return GNUNET_OK; - } - memcpy (&t6->ipv6_addr, - &((struct sockaddr_in6 *) addr)->sin6_addr, - sizeof (struct in6_addr)); - t6->u6_port = htons (plugin->port_inbound); - plugin->env->notify_address(plugin->env->cls,"http",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL); - } - return GNUNET_OK; -} - -int peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value) -{ - struct HTTP_PeerContext * pc = value; - struct Session * ps = pc->head; - struct Session * tmp = NULL; - struct HTTP_Message * msg = NULL; - struct HTTP_Message * msg_tmp = NULL; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing context for peer `%s'\n",GNUNET_i2s(&pc->identity)); - - while (ps!=NULL) - { - tmp = ps->next; - - GNUNET_free(ps->addr); - GNUNET_free(ps->url); - if (ps->msgtok != NULL) - GNUNET_SERVER_mst_destroy (ps->msgtok); - - msg = ps->pending_msgs_head; - while (msg!=NULL) - { - msg_tmp = msg->next; - GNUNET_free(msg); - msg = msg_tmp; - } - if (ps->direction==OUTBOUND) - { - if (ps->send_endpoint!=NULL) - curl_easy_cleanup(ps->send_endpoint); - if (ps->recv_endpoint!=NULL) - curl_easy_cleanup(ps->recv_endpoint); - } - - GNUNET_free(ps); - ps=tmp; - } - GNUNET_free(pc); - return GNUNET_YES; -} - /** * Exit point from the plugin. */ void * -libgnunet_plugin_transport_http_done (void *cls) +LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) { struct GNUNET_TRANSPORT_PluginFunctions *api = cls; struct Plugin *plugin = api->cls; CURLMcode mret; - GNUNET_assert(cls !=NULL); + if (plugin->http_server_daemon_v4 != NULL) + { + MHD_stop_daemon (plugin->http_server_daemon_v4); + plugin->http_server_daemon_v4 = NULL; + } + if (plugin->http_server_daemon_v6 != NULL) + { + MHD_stop_daemon (plugin->http_server_daemon_v6); + plugin->http_server_daemon_v6 = NULL; + } + if ( plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK) { GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4); @@ -1822,58 +2463,109 @@ libgnunet_plugin_transport_http_done (void *cls) plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK; } - if ( plugin->http_server_task_send != GNUNET_SCHEDULER_NO_TASK) + /* free all peer information */ + if (plugin->peers!=NULL) { - GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_send); - plugin->http_server_task_send = GNUNET_SCHEDULER_NO_TASK; + GNUNET_CONTAINER_multihashmap_iterate (plugin->peers, + &remove_peer_context_Iterator, + plugin); + GNUNET_CONTAINER_multihashmap_destroy (plugin->peers); } - - if (plugin->http_server_daemon_v4 != NULL) + if (plugin->multi_handle!=NULL) { - MHD_stop_daemon (plugin->http_server_daemon_v4); - plugin->http_server_daemon_v4 = NULL; + mret = curl_multi_cleanup(plugin->multi_handle); +#if DEBUG_HTTP + if ( CURLM_OK != mret) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed\n"); +#endif + plugin->multi_handle = NULL; } - if (plugin->http_server_daemon_v6 != NULL) + curl_global_cleanup(); + + if ( plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK) { - MHD_stop_daemon (plugin->http_server_daemon_v6); - plugin->http_server_daemon_v6 = NULL; + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task); + plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK; } - /* free all peer information */ - GNUNET_CONTAINER_multihashmap_iterate (plugin->peers, - &peer_context_Iterator, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (plugin->peers); - - mret = curl_multi_cleanup(plugin->multi_handle); - if ( CURLM_OK != mret) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed"); - + GNUNET_free_non_null (plugin->bind4_address); + GNUNET_free_non_null (plugin->bind6_address); + GNUNET_free_non_null(plugin->bind_hostname); +#if BUILD_HTTPS + GNUNET_free_non_null (plugin->crypto_init); + GNUNET_free_non_null (plugin->cert); + GNUNET_free_non_null (plugin->key); +#endif GNUNET_free (plugin); GNUNET_free (api); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unload http plugin complete...\n"); +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unload %s plugin complete...\n", PROTOCOL_PREFIX); +#endif return NULL; } +#if BUILD_HTTPS +static char * +load_certificate( const char * file ) +{ + struct GNUNET_DISK_FileHandle * gn_file; + + struct stat fstat; + char * text = NULL; + + if (0!=STAT(file, &fstat)) + return NULL; + text = GNUNET_malloc (fstat.st_size+1); + gn_file = GNUNET_DISK_file_open(file,GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_USER_READ); + if (gn_file==NULL) + { + GNUNET_free(text); + return NULL; + } + if (GNUNET_SYSERR == GNUNET_DISK_file_read(gn_file, text, fstat.st_size)) + { + GNUNET_free(text); + GNUNET_DISK_file_close(gn_file); + return NULL; + } + text[fstat.st_size] = '\0'; + GNUNET_DISK_file_close(gn_file); + + return text; +} +#endif + /** * Entry point for the plugin. */ void * -libgnunet_plugin_transport_http_init (void *cls) +LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) { struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; struct Plugin *plugin; struct GNUNET_TRANSPORT_PluginFunctions *api; struct GNUNET_TIME_Relative gn_timeout; long long unsigned int port; + char * component_name; +#if BUILD_HTTPS + char * key_file = NULL; + char * cert_file = NULL; +#endif GNUNET_assert(cls !=NULL); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n"); +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting %s plugin...\n", PROTOCOL_PREFIX); +#endif + GNUNET_asprintf(&component_name,"transport-%s",PROTOCOL_PREFIX); plugin = GNUNET_malloc (sizeof (struct Plugin)); + plugin->stats = env->stats; plugin->env = env; plugin->peers = NULL; + plugin->bind4_address = NULL; + plugin->use_ipv6 = GNUNET_YES; + plugin->use_ipv4 = GNUNET_YES; api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); api->cls = plugin; @@ -1886,61 +2578,281 @@ libgnunet_plugin_transport_http_init (void *cls) /* Hashing our identity to use it in URLs */ GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &plugin->my_ascii_hash_ident); + /* Use IPv6? */ + if (GNUNET_CONFIGURATION_have_value (env->cfg, + component_name, "USE_IPv6")) + { + plugin->use_ipv6 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, + component_name, + "USE_IPv6"); + } + /* Use IPv4? */ + if (GNUNET_CONFIGURATION_have_value (env->cfg, + component_name, "USE_IPv4")) + { + plugin->use_ipv4 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, + component_name,"USE_IPv4"); + } /* Reading port number from config file */ if ((GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (env->cfg, - "transport-http", + component_name, "PORT", &port)) || (port > 65535) ) { GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - "http", - _ - ("Require valid port number for transport plugin `%s' in configuration!\n"), - "transport-http"); - libgnunet_plugin_transport_http_done (api); + component_name, + _("Require valid port number for transport plugin `%s' in configuration!\n"), + PROTOCOL_PREFIX); + GNUNET_free(component_name); + LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); return NULL; } + + /* Reading ipv4 addresse to bind to from config file */ + if ((plugin->use_ipv4==GNUNET_YES) && (GNUNET_CONFIGURATION_have_value (env->cfg, + component_name, "BINDTO4"))) + { + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (env->cfg, + component_name, + "BINDTO4", + &plugin->bind_hostname)); + plugin->bind4_address = GNUNET_malloc(sizeof(struct sockaddr_in)); + plugin->bind4_address->sin_family = AF_INET; + plugin->bind4_address->sin_port = htons (port); + + if (inet_pton(AF_INET,plugin->bind_hostname, &plugin->bind4_address->sin_addr)<=0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + component_name, + _("Misconfigured address to bind to in configuration!\n")); + GNUNET_free(plugin->bind4_address); + GNUNET_free(plugin->bind_hostname); + plugin->bind_hostname = NULL; + plugin->bind4_address = NULL; + } + } + + /* Reading ipv4 addresse to bind to from config file */ + if ((plugin->use_ipv6==GNUNET_YES) && (GNUNET_CONFIGURATION_have_value (env->cfg, + component_name, "BINDTO6"))) + { + if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (env->cfg, + component_name, + "BINDTO6", + &plugin->bind_hostname)) + { + plugin->bind6_address = GNUNET_malloc(sizeof(struct sockaddr_in6)); + plugin->bind6_address->sin6_family = AF_INET6; + plugin->bind6_address->sin6_port = htons (port); + + if (inet_pton(AF_INET6,plugin->bind_hostname, &plugin->bind6_address->sin6_addr)<=0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + component_name, + _("Misconfigured address to bind to in configuration!\n")); + GNUNET_free(plugin->bind6_address); + GNUNET_free(plugin->bind_hostname); + plugin->bind_hostname = NULL; + plugin->bind6_address = NULL; + } + } + } + +#if BUILD_HTTPS + /* Reading HTTPS crypto related configuration */ + /* Get crypto init string from config */ + if (GNUNET_CONFIGURATION_have_value (env->cfg, + "transport-https", "CRYPTO_INIT")) + { + GNUNET_CONFIGURATION_get_value_string (env->cfg, + "transport-https", + "CRYPTO_INIT", + &plugin->crypto_init); + } + else + { + GNUNET_asprintf(&plugin->crypto_init,"NORMAL"); + } + +/* Get private key file from config */ + if (GNUNET_CONFIGURATION_have_value (env->cfg, + "transport-https", "KEY_FILE")) + { + GNUNET_CONFIGURATION_get_value_string (env->cfg, + "transport-https", + "KEY_FILE", + &key_file); + } + if (key_file==NULL) + GNUNET_asprintf(&key_file,"https.key"); + +/* Get private key file from config */ + if (GNUNET_CONFIGURATION_have_value (env->cfg,"transport-https", "CERT_FILE")) + { + GNUNET_CONFIGURATION_get_value_string (env->cfg, + "transport-https", + "CERT_FILE", + &cert_file); + } + if (cert_file==NULL) + GNUNET_asprintf(&cert_file,"https.cert"); + + /* read key & certificates from file */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading TLS certificate `%s' `%s'\n", key_file, cert_file); + + plugin->key = load_certificate( key_file ); + plugin->cert = load_certificate( cert_file ); + + if ((plugin->key==NULL) || (plugin->cert==NULL)) + { + char * cmd; + int ret = 0; + GNUNET_asprintf(&cmd,"gnunet-transport-certificate-creation %s %s", key_file, cert_file); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No usable TLS certificate found, creating certificate \n"); + ret = system(cmd); + + if (ret != 0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "https", + _("Could not create a new TLS certificate, shell script `%s' failed!\n"),cmd, + "transport-https"); + GNUNET_free (key_file); + GNUNET_free (cert_file); + GNUNET_free (component_name); + + LIBGNUNET_PLUGIN_TRANSPORT_DONE(api); + GNUNET_free (cmd); + return NULL; + } + + GNUNET_free (cmd); + + plugin->key = load_certificate( key_file ); + plugin->cert = load_certificate( cert_file ); + + if ((plugin->key==NULL) || (plugin->cert==NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "https", + _("No usable TLS certificate found and creating one failed! \n"), + "transport-https"); + GNUNET_free (key_file); + GNUNET_free (cert_file); + GNUNET_free (component_name); + + LIBGNUNET_PLUGIN_TRANSPORT_DONE(api); + return NULL; + } + } + GNUNET_free (key_file); + GNUNET_free (cert_file); + + GNUNET_assert((plugin->key!=NULL) && (plugin->cert!=NULL)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n"); +#endif + GNUNET_assert ((port > 0) && (port <= 65535)); plugin->port_inbound = port; gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT; - if ((plugin->http_server_daemon_v4 == NULL) && (plugin->http_server_daemon_v6 == NULL) && (port != 0)) - { - plugin->http_server_daemon_v6 = MHD_start_daemon (MHD_USE_IPv6, + unsigned int timeout = (gn_timeout.value) / 1000; + if ((plugin->http_server_daemon_v6 == NULL) && (plugin->use_ipv6 == GNUNET_YES) && (port != 0)) + { + struct sockaddr * tmp = (struct sockaddr *) plugin->bind6_address; + plugin->http_server_daemon_v6 = MHD_start_daemon ( +#if DEBUG_MHD + MHD_USE_DEBUG | +#endif +#if BUILD_HTTPS + MHD_USE_SSL | +#endif + MHD_USE_IPv6, port, - &acceptPolicyCallback, - plugin , &accessHandlerCallback, plugin, - MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16, - MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1, - MHD_OPTION_CONNECTION_TIMEOUT, (gn_timeout.value / 1000), - MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024), - MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL, + &mhd_accept_cb, + plugin , &mdh_access_cb, plugin, + MHD_OPTION_SOCK_ADDR, tmp, + MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32, + //MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6, +#if BUILD_HTTPS + MHD_OPTION_HTTPS_PRIORITIES, plugin->crypto_init, + MHD_OPTION_HTTPS_MEM_KEY, plugin->key, + MHD_OPTION_HTTPS_MEM_CERT, plugin->cert, +#endif + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (2 * GNUNET_SERVER_MAX_MESSAGE_SIZE), + MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL, + MHD_OPTION_EXTERNAL_LOGGER, mhd_logger, plugin->mhd_log, MHD_OPTION_END); - plugin->http_server_daemon_v4 = MHD_start_daemon (MHD_NO_FLAG, + } + if ((plugin->http_server_daemon_v4 == NULL) && (plugin->use_ipv4 == GNUNET_YES) && (port != 0)) + { + plugin->http_server_daemon_v4 = MHD_start_daemon ( +#if DEBUG_MHD + MHD_USE_DEBUG | +#endif +#if BUILD_HTTPS + MHD_USE_SSL | +#endif + MHD_NO_FLAG, port, - &acceptPolicyCallback, - plugin , &accessHandlerCallback, plugin, - MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16, - MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1, - MHD_OPTION_CONNECTION_TIMEOUT, (gn_timeout.value / 1000), - MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024), - MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL, + &mhd_accept_cb, + plugin , &mdh_access_cb, plugin, + MHD_OPTION_SOCK_ADDR, (struct sockaddr_in *)plugin->bind4_address, + MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32, + //MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6, +#if BUILD_HTTPS + MHD_OPTION_HTTPS_PRIORITIES, plugin->crypto_init, + MHD_OPTION_HTTPS_MEM_KEY, plugin->key, + MHD_OPTION_HTTPS_MEM_CERT, plugin->cert, +#endif + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (2 * GNUNET_SERVER_MAX_MESSAGE_SIZE), + MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL, + MHD_OPTION_EXTERNAL_LOGGER, mhd_logger, plugin->mhd_log, MHD_OPTION_END); - } + } if (plugin->http_server_daemon_v4 != NULL) plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4); if (plugin->http_server_daemon_v6 != NULL) plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6); + if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 on port %u\n",port); - else if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 on port %u\n",port); + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address",port); +#endif + } + else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) && (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)) + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv6 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address", port); +#endif + } + else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) && (plugin->http_server_task_v4 == GNUNET_SCHEDULER_NO_TASK)) + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address", port); +#endif + } else { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No MHD was started, transport plugin not functional!\n"); - libgnunet_plugin_transport_http_done (api); + char * tmp = NULL; + if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_YES)) + GNUNET_asprintf(&tmp,"with IPv4 and IPv6 enabled"); + if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_YES)) + GNUNET_asprintf(&tmp,"with IPv4 enabled"); + if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_NO)) + GNUNET_asprintf(&tmp,"with IPv6 enabled"); + if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_NO)) + GNUNET_asprintf(&tmp,"with NO IP PROTOCOL enabled"); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR,"HTTP Server with %s could not be started on port %u! %s plugin failed!\n",tmp, port, PROTOCOL_PREFIX); + GNUNET_free (tmp); + GNUNET_free (component_name); + LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); return NULL; } @@ -1951,16 +2863,18 @@ libgnunet_plugin_transport_http_init (void *cls) if ( NULL == plugin->multi_handle ) { GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - "http", - _("Could not initialize curl multi handle, failed to start http plugin!\n"), - "transport-http"); - libgnunet_plugin_transport_http_done (api); + component_name, + _("Could not initialize curl multi handle, failed to start %s plugin!\n"), + PROTOCOL_PREFIX); + GNUNET_free(component_name); + LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); return NULL; } plugin->peers = GNUNET_CONTAINER_multihashmap_create (10); GNUNET_OS_network_interfaces_list (&process_interfaces, plugin); + GNUNET_free(component_name); return api; }